Conditional Access and Geographic Filtering in Nginx
Link Authentication with secure_link
This module validates requests by checking a cryptographic hash and expiration timestamp, preventing hot-linking and timed access to resources. It must be compiled into Nginx beforehand.
Directives
secure_link expression– Defines how to extract MD5 hash and expiration from the request (context:http,server,location).secure_link_md5 expression– Builds the server-side digest string for comparison (context:http,server,location).
Example configuration (secure_dl.conf):
server {
listen 80;
server_name static.example.com;
root /data/files;
location /protected {
secure_link $arg_token,$arg_expiry;
secure_link_md5 "$secure_link_expires$uri secret123";
if ($secure_link = "") {
return 403;
}
if ($secure_link = "0") {
return 410;
}
}
}
A backend script generates the token. Below is a shell variant using OpenSSL:
#!/bin/sh
HOST="static.example.com"
RESOURCE="/protected/report.pdf"
SECRET="secret123"
EXPIRY=$(date -d "2025-12-20 23:59:59" +%s)
RAW="${EXPIRY}${RESOURCE} ${SECRET}"
HASH=$(echo -n "$RAW" | openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =)
echo "https://${HOST}${RESOURCE}?token=${HASH}&expiry=${EXPIRY}"
A valid request looks like:
https://static.example.com/protected/report.pdf?token=abc123DEF&expiry=1766275199
Expired or tampered links return 410 or 403 respectively.
IP Geolocation Using geoip
This module reads the MaxMind GeoIP binary database to determine the country and city of a client IP. Its useful for region-based access control or logging. The module is typically provided as a dynamic extension.
Setup
Install the module package and load it:
yum install nginx-module-geoip
Download the GeoLite2 databases (legacy dat format shown; ensure correct paths):
mkdir -p /etc/nginx/geoip
cd /etc/nginx/geoip
wget https://git.io/GeoLiteCountry.dat.gz && gunzip GeoLiteCountry.dat.gz
wget https://git.io/GeoLiteCity.dat.gz && gunzip GeoLiteCity.dat.gz
Enable the module in nginx.conf and define a configuration with access rules:
load_module modules/ngx_http_geoip_module.so;
geoip_country /etc/nginx/geoip/GeoIP.dat;
geoip_city /etc/nginx/geoip/GeoLiteCity.dat;
server {
listen 80;
server_name localhost;
location / {
if ($geoip_country_code != "CN") {
return 403;
}
root /usr/share/nginx/html;
index index.html;
}
location /geo_info {
default_type text/plain;
return 200 "$remote_addr $geoip_country_name $geoip_country_code $geoip_city";
}
}
The first location denies access to clients out side China (CN). The /geo_info endpoint echoes the resolved geographic data. When testing through proxies, ensure the $remote_addr reflects the real client (e.g., via real_ip_header).