Why Trailing Slashes Change Nginx Proxy Behavior and How to Deploy WASM
This article explores common pitfalls and nuanced behaviors in Nginx configuration, illustrating how trailing slashes affect proxy_pass routing, detailing location matching priorities, comparing reload commands, guiding offline installation across architectures, and providing comprehensive settings for deploying Unity WebGL WASM applications with proper MIME types and security headers.
The author shares several practical Nginx issues encountered during daily use, turning them into a concise tutorial.
Problem 1 – Proxy Pass and Trailing Slashes
Two similar location /test blocks differ only by the trailing slash after the proxy_pass URL:
# Config 1
location /test {
proxy_pass 'http://192.186.0.1:8080';
}
# Config 2
location /test {
proxy_pass 'http://192.186.0.1:8080/';
}When the request path is /test/file/getList, Config 1 forwards it as http://192.186.0.1:8080/test/file/getList (the /test prefix is kept), while Config 2 forwards it as http://192.186.0.1:8080/file/getList (the prefix is stripped). The presence of the trailing slash in proxy_pass determines whether the location prefix is retained.
Although the short form works, it can be confusing for newcomers. A clearer alternative uses an explicit rewrite to drop the prefix:
# Recommended rewrite
location /test {
rewrite ^/test/(.*)$ /$1 break;
proxy_pass 'http://192.186.0.1:8080';
}This makes the intent obvious and improves readability.
Problem 2 – Reloading Nginx
Two common commands reload the server: nginx -s reload – Sends a reload signal directly to the Nginx master process; it is graceful (existing connections stay alive) and works as long as the nginx binary is in $PATH. systemctl reload nginx – Uses systemd to reload the service; it is the recommended method on modern Linux distributions (CentOS 7/8, RHEL 7/8, Ubuntu 16.04+).
Both commands achieve a graceful reload, but systemctl also integrates with service management (enable on boot, status checks, etc.). A typical workflow is:
# Test configuration first
nginx -t
# If OK, reload via systemd
systemctl reload nginx
# Verify status
systemctl status nginx
# Fallback if systemd is unavailable
sudo nginx -s reloadProblem 3 – Offline Installation of Nginx
When a server cannot access the internet, install Nginx from pre‑compiled packages:
Identify the architecture with uname -m (e.g., x86_64, aarch64).
Download the appropriate RPM or DEB package on a machine with internet access, e.g.:
# x86_64 (CentOS 7)
wget http://nginx.org/packages/centos/7/x86_64/RPMS/nginx-1.24.0-1.el7.ngx.x86_64.rpm
# ARM64 (Ubuntu Jammy)
wget http://nginx.org/packages/ubuntu/pool/nginx/n/nginx/nginx_1.24.0-1~jammy_arm64.debTransfer the package to the target server and install it:
# RPM
sudo rpm -ivh nginx-*.rpm
# DEB
sudo dpkg -i nginx-*.debStart and enable the service with systemctl start nginx and systemctl enable nginx, then verify with nginx -v and a curl request.
Problem 4 – Deploying Unity WebGL (WASM) with Nginx
WASM files must be served with the correct MIME type; otherwise browsers reject them. Add the following line to mime.types (or inside a types block):
application/wasm wasm;A complete server block for a Unity WebGL build might look like this (paths and domain should be adjusted):
server {
listen 80;
server_name your-domain.com;
root /var/www/unity-webgl;
index index.html;
charset utf-8;
# WASM files (plain, gzipped, brotli)
location ~ \.wasm$ {
types { application/wasm wasm; }
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Access-Control-Allow-Origin *;
}
location ~ \.wasm\.gz$ {
add_header Content-Encoding gzip;
add_header Content-Type application/wasm;
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Access-Control-Allow-Origin *;
}
location ~ \.wasm\.br$ {
add_header Content-Encoding br;
add_header Content-Type application/wasm;
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Access-Control-Allow-Origin *;
}
# Data files
location ~ \.data$ { types { application/octet-stream data; } add_header Cache-Control "public, max-age=31536000, immutable"; }
location ~ \.data\.gz$ { add_header Content-Encoding gzip; add_header Content-Type application/octet-stream; add_header Cache-Control "public, max-age=31536000, immutable"; }
location ~ \.data\.br$ { add_header Content-Encoding br; add_header Content-Type application/octet-stream; add_header Cache-Control "public, max-age=31536000, immutable"; }
# JavaScript (plain, gz, br)
location ~ \.js$ { types { application/javascript js; } add_header Cache-Control "public, max-age=31536000, immutable"; }
location ~ \.js\.gz$ { add_header Content-Encoding gzip; add_header Content-Type application/javascript; add_header Cache-Control "public, max-age=31536000, immutable"; }
location ~ \.js\.br$ { add_header Content-Encoding br; add_header Content-Type application/javascript; add_header Cache-Control "public, max-age=31536000, immutable"; }
# Static assets (images, CSS, etc.)
location ~* \.(jpg|jpeg|png|gif|ico|svg)$ { add_header Cache-Control "public, max-age=2592000"; }
location ~* \.css$ { add_header Content-Type text/css; add_header Cache-Control "public, max-age=2592000"; }
# HTML – no cache to ensure updates are seen immediately
location ~ \.html$ { add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Pragma "no-cache"; add_header Expires "0"; }
# Root fallback
location / { try_files $uri $uri/ /index.html; }
# Gzip compression for common types, including WASM
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/x-javascript application/xml application/xml+rss application/wasm application/octet-stream;
# Basic security headers
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "SAMEORIGIN";
# Deny hidden files and backup files
location ~ /\. { deny all; }
location ~ ~$ { deny all; }
}Key take‑aways for WASM deployment:
Register application/wasm in mime.types.
Provide separate blocks for uncompressed, gzip‑compressed, and brotli‑compressed WASM files.
Set long‑term cache headers for static assets while disabling caching for HTML.
Enable Gzip (or Brotli) compression for faster delivery.
Add basic security headers and block access to hidden or backup files.
Location Matching Priority Cheat‑Sheet
The order Nginx evaluates location directives is:
Exact match ( =) – highest priority.
Prefix with ^~ – stops further regular‑expression checks.
Regular expressions ( ~ and ~*) – evaluated in the order they appear.
Plain prefixes – the longest matching prefix wins.
This rule explains why /test/ matches a location /test/ block instead of a generic location /test when both could apply.
Overall, the article consolidates practical Nginx knowledge: handling subtle proxy_pass differences, choosing the right reload command, installing Nginx offline, and configuring a robust server block for Unity WebGL/WASM applications.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
