Common patterns cheatsheet.
www → apex
server {
listen 80;
listen 443 ssl;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
HTTP → HTTPS
server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
Trailing slash redirect
rewrite ^/path$ /path/ permanent; # add trailing slash
rewrite ^(.+)/$ $1 permanent; # remove trailing slash
Force lowercase URLs
if ($request_uri ~ [A-Z]) {
return 301 $scheme://$host$1$lowercase_uri;
}
Better: OpenResty Lua to lowercase.
Block specific path
location /admin {
return 404;
}
location ~ \.(env|git) {
deny all;
log_not_found off;
}
Internal proxy + public path
location /api/ {
proxy_pass http://internal:8000/;
}
location = /api/admin {
return 404; # hide admin path
}
Status code customizations
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
error_page 403 = @ratelimited;
location @ratelimited {
return 429;
}
Mirror traffic
location / {
mirror /mirror;
mirror_request_body on;
proxy_pass http://primary;
}
location = /mirror {
internal;
proxy_pass http://shadow$request_uri;
proxy_set_header X-Original-URI $request_uri;
}
Send copy to shadow for testing without affecting client.
A/B testing
split_clients $request_id $variant {
50% A;
* B;
}
map $variant $upstream {
A app-stable;
B app-canary;
}
location / { proxy_pass http://$upstream; }
Forwarding to external
location /github/ {
proxy_pass https://api.github.com/;
proxy_set_header Host api.github.com;
proxy_set_header User-Agent "MyProxy/1.0";
resolver 1.1.1.1 valid=300s;
}
Hot reload static
location ~* \.(css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location = /index.html {
add_header Cache-Control "no-cache";
}
Maintenance page
location / {
if ($cookie_admin = "") {
return 503;
}
proxy_pass http://app;
}
error_page 503 @maintenance;
location @maintenance {
root /var/www;
try_files /maintenance.html =503;
add_header Retry-After 3600;
}
admin cookie bypasses for ops.
Redirect old paths
location = /old { return 301 /new; }
map $request_uri $redirect {
/old1 /new1;
/old2 /new2;
}
if ($redirect) {
return 301 $redirect;
}
Conditional CSP
map $sent_http_content_type $csp {
default "default-src 'self'";
text/html "default-src 'self'; script-src 'self' 'nonce-$request_id'";
}
add_header Content-Security-Policy $csp always;
Pass real IP
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
Now $remote_addr is real client IP.
Block by country (GeoIP2)
geoip2 /etc/nginx/GeoLite2-Country.mmdb {
$geoip2_country_code country iso_code;
}
map $geoip2_country_code $blocked {
default 0;
KP 1;
}
server {
if ($blocked) { return 451; }
}
Default catchall server
server {
listen 80 default_server;
listen 443 ssl default_server;
server_name _;
ssl_certificate /etc/nginx/snakeoil.crt;
ssl_certificate_key /etc/nginx/snakeoil.key;
return 444; # close without response
}
Drops requests with wrong Host header.
Multiple domains, same backend
server {
server_name example.com a.example.com b.example.com;
location / { proxy_pass http://app; }
}
Sub-applications
location /blog/ {
proxy_pass http://blog-app/;
}
location /shop/ {
proxy_pass http://shop-app/;
}
location /api/ {
proxy_pass http://api-app/;
}
Common mistakes
ifinsidelocation— nginx wiki: “If is evil.” Usemaporreturnwhen possible.- Forgetting trailing
/in proxy_pass. return 301to relative URL — must be absolute.- Caching maintenance page (cache control matters).
- Multiple redirects for same path causing loop.
Read this next
If you want my pattern collection, it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .