diff --git a/Changelog.md b/Changelog.md
new file mode 100644
--- /dev/null
+++ b/Changelog.md
@@ -0,0 +1,3 @@
+# 1.0.0
+
+* Initial release. Support setting the `Access-Control-Allow-Origin` and `Access-Control-Allow-Credentials` headers.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2013, Oliver Charles
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Oliver Charles nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/snap-cors.cabal b/snap-cors.cabal
new file mode 100644
--- /dev/null
+++ b/snap-cors.cabal
@@ -0,0 +1,38 @@
+name:                snap-cors
+version:             1.0.0
+synopsis:            Add CORS headers to Snap applications
+description:
+  Add CORS (cross-origin resource sharing) headers to Snap applications. This
+  enables web applications running on other domains to make requests against
+  another application.
+  .
+  Currently this package provides support for setting the
+  @Access-Control-Allow-Origin@ and @Access-Control-Allow-Credentials@ headers,
+  it does not yet do pre-flighting. If you need this, please open an issue on
+  Github and I'll fix it ASAP (otherwise I will add this feature as time permits).
+
+homepage:            http://github.com/ocharles/snap-cors
+license:             BSD3
+license-file:        LICENSE
+author:              Oliver Charles
+maintainer:          ollie@ocharles.org.uk
+copyright:           Oliver Charles (c) 2013
+category:            Web
+build-type:          Simple
+cabal-version:       >=1.8
+extra-source-files:
+  Changelog.md
+
+library
+  hs-source-dirs: src
+  exposed-modules:
+    Snap.CORS
+  build-depends:
+    base >= 4.5 && < 5,
+    hashable >= 1.1 && <1.2,
+    network >= 2.4 && < 2.5,
+    snap >= 0.13 && < 0.14,
+    text >= 0.11 && < 0.12,
+    transformers >= 0.3 && < 0.4,
+    unordered-containers >= 0.2 && <0.3
+  ghc-options: -Wall
diff --git a/src/Snap/CORS.hs b/src/Snap/CORS.hs
new file mode 100644
--- /dev/null
+++ b/src/Snap/CORS.hs
@@ -0,0 +1,139 @@
+{-# LANGUAGE OverloadedStrings #-}
+-- | Add <http://www.w3.org/TR/cors/ CORS> (cross-origin resource sharing)
+-- headers to a Snap application. CORS headers can be added either conditionally
+-- or unconditionally to the entire site, or you can apply CORS headers to a
+-- single route.
+module Snap.CORS
+  ( -- * Wrappers
+    wrapCORS
+  , wrapCORSWithOptions
+
+  -- * Applying CORS to a specific response
+  , applyCORS
+
+    -- * Option Specification
+  , CORSOptions(..)
+  , defaultOptions
+
+    -- ** Origin lists
+  , OriginList(..)
+  , OriginSet, mkOriginSet, origins
+                            
+    -- * Internals
+  , HashableURI(..)
+  ) where
+
+import Control.Applicative
+import Control.Monad (guard, mzero, void, when)
+import Control.Monad.Trans.Class (lift)
+import Control.Monad.Trans.Maybe (MaybeT(..))
+import Data.Hashable (Hashable(..))
+import Data.Text.Encoding (decodeUtf8, encodeUtf8)
+import Network.URI (URI (..), URIAuth (..),  parseURI)
+
+import qualified Data.HashSet as HashSet
+import qualified Data.Text as Text
+import qualified Snap
+
+-- | A set of origins. RFC 6454 specifies that origins are a scheme, host and
+-- port, so the 'OriginSet' wrapper around a 'HashSet.HashSet' ensures that each
+-- 'URI' constists of nothing more than this.
+newtype OriginSet = OriginSet { origins :: HashSet.HashSet HashableURI }
+
+-- | Used to specify the contents of the @Access-Control-Allow-Origin@ header.
+data OriginList
+  = Everywhere
+  -- ^ Allow any origin to access this resource. Corresponds to
+  -- @Access-Control-Allow-Origin: *@ 
+  | Nowhere
+  -- ^ Do not allow cross-origin requests    
+  | Origins OriginSet
+  -- ^ Allow cross-origin requests from these origins.
+
+-- | Specify the options to use when building CORS headers for a response. Most
+-- of these options are 'Snap.Handler' actions to allow you to conditionally
+-- determine the setting of each header.
+data CORSOptions m = CORSOptions
+  { corsAllowOrigin :: m OriginList
+  -- ^ Which origins are allowed to make cross-origin requests.
+
+  , corsAllowCredentials :: m Bool
+  -- ^ Whether or not to allow exposing the response when the omit credentials
+  -- flag is unset.  
+  }
+
+-- | Liberal default options. Specifies that all origins may make cross-origin
+-- requests, allow-credentials is true. Headers are determined unconditionally.
+defaultOptions :: Monad m => CORSOptions m
+defaultOptions = CORSOptions
+  { corsAllowOrigin = return Everywhere
+  , corsAllowCredentials = return True
+  }
+
+-- | Apply CORS for every request, unconditionally.
+--
+-- 'wrapCors' ≡ 'wrapCORSWithOptions' 'defaultOptions'
+wrapCORS :: Snap.Initializer b v ()
+wrapCORS = wrapCORSWithOptions defaultOptions
+
+-- | Initialize CORS for all requests with specific options.
+wrapCORSWithOptions :: CORSOptions (Snap.Handler b v) -> Snap.Initializer b v ()
+wrapCORSWithOptions options = Snap.wrapSite (applyCORS options >>)
+
+-- | Apply CORS headers to a specific request. This is useful if you only have
+-- a single action that needs CORS headers, and you don't want to pay for
+-- conditional checks on every request.
+applyCORS :: Snap.MonadSnap m => CORSOptions m -> m ()
+applyCORS options = void $ runMaybeT $ do
+  origin <- MaybeT $ Snap.getsRequest (Snap.getHeader "Origin")
+  originUri <- MaybeT $ pure $
+    fmap simplifyURI $ parseURI $ Text.unpack $ decodeUtf8 origin
+
+  originList <- lift $ corsAllowOrigin options
+
+  case originList of
+    Everywhere -> return ()
+    Nowhere -> mzero
+    (Origins (OriginSet xs)) ->
+      guard (HashableURI originUri `HashSet.member` xs)
+
+  lift $ do
+    addHeader "Access-Control-Allow-Origin"
+              (encodeUtf8 $ Text.pack $ show originUri)
+    allowCredentials <- corsAllowCredentials options
+    when (allowCredentials) $
+      addHeader "Access-Control-Allow-Credentials" "true"
+
+ where
+  addHeader k v = Snap.modifyResponse (Snap.addHeader k v)
+
+mkOriginSet :: [URI] -> OriginSet
+mkOriginSet = OriginSet . HashSet.fromList . map (HashableURI . simplifyURI)
+
+simplifyURI :: URI -> URI
+simplifyURI uri = uri { uriAuthority = fmap simplifyURIAuth (uriAuthority uri)
+                       , uriPath = ""
+                       , uriQuery = ""
+                       , uriFragment = ""
+                       }
+ where simplifyURIAuth auth = auth { uriUserInfo = "" }
+
+-- | A @newtype@ over 'URI' with a 'Hashable' instance.
+newtype HashableURI = HashableURI URI
+  deriving (Eq, Show)
+
+instance Hashable HashableURI where
+  hashWithSalt s (HashableURI (URI scheme authority path query fragment)) =
+    s `hashWithSalt`
+    scheme `hashWithSalt`
+    fmap hashAuthority authority `hashWithSalt`
+    path `hashWithSalt`
+    query `hashWithSalt`
+    fragment
+
+   where
+    hashAuthority (URIAuth userInfo regName port) =
+          s `hashWithSalt`
+          userInfo `hashWithSalt`
+          regName `hashWithSalt`
+          port
