Ring middleware for IP whitelisting
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.



Ring middleware that allows you to protect routes with an IP whitelist.


Add the following dependency to your project.clj

[net.danielcompton/ring-ip-whitelist "0.2.1"]

Dependencies Status


To IP whitelist routes, wrap them with wrap-ip-whitelist. wrap-ip-whitelist support IPv4 and IPv6 IP addresses.

(ns my.cool-ns
  (:require [ring.middleware.ip-whitelist :as ip]))

(def app
  (-> handler
      (ip/wrap-ip-whitelist {:cidrs ["" "" "::1/128"})))

wrap-ip-whitelist takes a map of configuration options. Only :cidrs is required.

  • :cidrs: A whitelist of CIDR strings or IP addresses. IP addresses are treated as a CIDR for a single address (e.g. a /32 for an IPv4 address). If the list is inside an atom then the whitelist will be updated when the atom changes.
  • :ip-fn: An optional function to extract the IP address. Defaults to :remote-addr.
  • :error-response: An optional function that returns a response if the request is not in the whitelist. Defaults to a 403 Not Authorized response.
  • :allow-access?: An optional function that can be called if IP whitelisting fails but more checking is needed before denying access. Takes a request argument, returns truthy if the request should be allowed, otherwise it will be denied."

CIDR ranges shouldn't be too large. The current implementation generates every IP address in the CIDR range. This is fine for a smallish number of IP addresses in the whitelist, and keeps the implementation simple and fast. The tradeoff is memory space. Don't use this to whitelist the whole world unless you want to run out of memory very shortly afterwards.

Passing :cidrs as an atom

(def ip-whitelist (atom [""]))
(-> routes
    (ip/wrap-ip-whitelist {:cidrs ip-whitelist}))

(swap! ip-whitelist conj "")

;; Middleware now whitelists these IPs
;; ["", "", "", "", ""]


In almost all cases you should use something like ring.middleware.proxy-headers/wrap-forwarded-remote-addr to translate proxy headers into :remote-addr for you. If for some reason you do need to look at other headers to find the 'real' IP address of a client then you can do it here. :ip-fn takes a Ring request and returns an IP address string.


If people fail your IP whitelist, you may want to return a customised 403 error page that matches your sites theme, or a 404 so you don't reveal that the user wasn't authorised to access a protected route. You can do this by passing an :error-response function to override the default. :error-response takes the failed Ring request, and returns a Ring response.

(-> routes
    (wrap-ip-whitelist {:cidrs          [""]
                        :error-response (constantly
                                          {:status  404
                                           :headers {"Content-Type" "text/html"}
                                           :body    "<h1>Not found</h1>"})}))


Use :allow-access? if you need to do additional checking before denying someone access. For example you may want to allow access from within your internal network to anyone, but outside users have to be authenticated.

(-> routes
    (ip/wrap-ip-whitelist {:cidrs [""]
                           :allow-access (fn [req] (check-cookies-to-see-if-authorised req))}))

Security of IP whitelisting

While IP whitelisting increases the difficulty for an attacker, if an attacker controls a router between a whitelisted IP and the server they can bypass your IP whitelisting. Also, if they can get onto the network of one of the whitelisted ranges then it is also game over. Treat IP whitelisting as an additional layer of security, not your only one. Some useful links to read more on IP whitelisting:

Protect some of your routes with IP whitelisting

TODO: Show how you can protect just some of your routes before they are combined.
TODO: Where to put this in your stack of middleware.


Copyright © 2016 Daniel Compton

This project is released under the MIT License