Nginx by examples: reverse proxy
Nginx is primarily designed as a reverse proxy and can add a lot of value if placed in front of your applications.
It can be configured to handle:
- HTTPS termination
- HTTP2 termination
- WAF protection
- DOS protection
- Caching
- Basic/Digest Authentication
Let’s see how we can setup a basic Java API webapp behind Nginx
server {
# custom virtual host root folder
root /var/www/mydomain.com/web/;
# index file (when a folder URI is matched)
index index.html;
# server name (for virtual host resolution)
server_name mydomain.com www.mydomain.com;
# custom access and error log
access_log /var/www/mydomain.com/log/mydomain.com.access;
error_log /var/www/mydomain.com/log/mydomain.com.error error;
# the default catchall block
location / {
# this will return a 403 http error code
deny all;
}
# we server some static welcome page if present on a / request
location =/ {
try_files /index.html =404;
}
# our apis are under /api
location /api {
# we tunnel through to the java web app living on localhost
proxy_pass http://127.0.0.1:8080;
# we tweak the send and receive timeout (especially if our backend can be a bit slow)
proxy_send_timeout 600;
proxy_read_timeout 600;
}
}
Pretty straightforward.
If we have multiple apps hosted on the same server we can simply add another location
block as follows
# our documentation is dynamically generated and served by another web app
location /docs {
proxy_pass http://127.0.0.1:9000;
# we intercept all 302 redirects to prefix all URIs with /docs/
# after the response has been generated by the downstream server
proxy_redirect / /docs/;
}
The use of proxy_redirect
is quite handy when the proxied app has no knowledge of the URL its content is actually served from.
Proxying 3rd party servers
If we need to proxy content from another server (from Amazon S3 for example) the configuration has to be extended a bit
location /docs {
# we force http/1.1 to take advantage of keep alive sockets
proxy_http_version 1.1;
# we replace the host header (for correct virtual host resolution)
proxy_set_header Host $s3_bucket;
# we hide some S3 specific headers
proxy_hide_header x-amz-id-2;
proxy_hide_header x-amz-request-id;
proxy_hide_header Set-Cookie;
# we can discard any cookie set by Amazon
proxy_ignore_headers "Set-Cookie";
# we serve the nginx error pages (as opposed to the S3 ones)
proxy_intercept_errors on;
# We force the method to GET in the downstream server to prevent
# POST/PUT/DELETE requests against S3 that would result in errors
proxy_method GET;
# we setup a DNS resolver using Google open DNS servers
resolver 8.8.8.8;
resolver_timeout 10s;
# we finally tunnel the request
proxy_pass http://your_bucket.s3.amazonaws.com/suburi;
}
Notes
-
When proxy_passing to a 3rd party server it’s always a good idea to modify the
host
header to make sure that the virtual host resolution on the downstream server works as it should -
A DNS resolver is always required when specifying downstream servers by domain name. Nginx does not rely on the gethostbyname() system call as it provides a more performing native DNS client implementation.
- Typical values for DNS resolution are Google DNS servers (
8.8.8.8
or8.8.4.4
)
- Typical values for DNS resolution are Google DNS servers (
-
While proxying requests to a downstream server nginx will match and replace the incoming URI (
/docs
) with the one provided in theproxy_pass
directive (if any)- So for example
/docs/images/image.gif
will cause nginx to requesthttp://your_bucket.s3.amazonaws.com/suburi/images/image.gif
- So for example
-
The http request to the downstream servers can be extensively manipulated by altering headers, GET parameters and modifying the body of the request. In a similar way it is also possible to manipulate the response served to the client.