Building a Secure HTTP Upload Endpoint with Nginx and Docker

Secure HTTP Upload Endpoint with Nginx in a Docker Container
In many teams I’ve worked with, technical choices for file upload services were made without considering long-term maintenance and security. Usually the implementation time would be reasonable, but the maintenance effort afterwards is neglected.
This blog demonstrates how to create a secure HTTP upload endpoint with Nginx in Docker, focusing on low-maintenance architecture instead of just quick implementation. It’s a nice bonus to solve this case using good old NginX!
For a less technical blog about keeping focus on control, read my other blog about IT Minimalism: https://www.infosupport.com/it-minimalism-simple-solutions-for-sustainable-it-systems
Context: Why Maintenance Matters
I’m usually operating in the context of teams that provide long-time services to customers and other teams within the same organisation.
The effort of building a solution to a problem is usually way cheaper than maintaining that solution. So maintenance is the focus.
In this example the context is as follows:
- Every week, another team delivers a file to us.
- We needed a safe, secure file upload endpoint that would remain reliable over time with minimal development effort.
- There is limited software development capability in our team.
Solution: Using NginX for HTTP Uploads
We chose Nginx because it is a widely adopted, battle-tested solution for handling HTTP requests and file uploads. When deployed inside a Docker container, it becomes an isolated, low-maintenance solution with a minimal attack surface. It is well maintained by the vendor and has a low attack surface (if configured well).
Configuration: Secure NginX Setup for storing upload data
Nginx configuration allows storing HTTP POST request body content directly to disk while proxying requests to a backend. This makes it an efficient choice for a secure HTTP upload endpoint. You will need a location with configuration like this:
# only traffic that exactly points to '/example' will be handled here location = /example { # Only allow the POST method, deny all other methods. if ($request_method != POST) { return 404; } ############################################################################################ # HTTP Request Limit Module : https://nginx.org/en/docs/http/ngx_http_limit_req_module.html ############################################################################################ # Limit the amount of requests per minute limit_reqzone=globalzone burst=10 nodelay; ########################################################################################## # HTTP Auth Basic Module : https://nginx.org/en/docs/http/ngx_http_auth_basic_module.html ########################################################################################## # Enables validation of user name and password using “HTTP Basic Authentication” auth_basic "closed site"; # Specify a file that keeps usernames and passwords auth_basic_user_file /mnt/secrets/example; ############################################################################## # HTTP Core Module : https://nginx.org/en/docs/http/ngx_http_core_module.html ############################################################################## # Determines whether nginx should save the entire client request body into a file client_body_in_file_only on; # Determines whether nginx should save the entire client request body in a single buffer client_body_in_single_buffer on; # Defines a directory for storing temporary files holding client request bodies client_body_temp_path /mnt/output/example; # Defines a timeout for reading client request body client_body_timeout 60s; # Sets the maximum allowed size of the client request body client_max_body_size 50M; # A proxy pass is needed, otherwise the client body is not saved to disk proxy_pass 'http://localhost/proxy_pass_local_endpoint'; }
- Only allow POST requests being made to ‘/example’
- Use basic authentication to only enable specific users to post new information
- Limiting the amount of requests to prevent denial of service and password brute forcing using ‘limit_reqzone’.
In order for this to work, you will need server configuration like this:limit_req_zone global zone=globalzone:1m rate=10r/m;
The configuration above depends on the local endpoint that ‘handles’ the request, so we add a minimal config for that:
# only traffic that exactly points to '/proxy_pass_local_endpoint' will be handled here location = /proxy_pass_local_endpoint { # Only allow traffic from within nginx, deny everything else. if ($remote_addr = 127.0.0.1) { return 200 "Thanks for uploading this content!"; } return 404; }
Docker benefits
We run this Nginx HTTP upload service in Docker to keep services isolated, improve DevOps clarity, and make updates easy. A Dockerized Nginx endpoint ensures security patches are always up to date, while keeping the attack surface minimal. The benefits of adding Docker to encapsulate this solution are:
- Updates -> An Up-To-Date NginX Docker Image is available, see: https://hub.docker.com/_/nginx
- Clarity -> It is easy to find out what is running on your system.
- Resource Scoping -> If you ever need to remove this solution, just remove the container and you are sure the application is deleted from your system. The docker container explicitly mentions the directories on disk being used as a volume.
- Security -> You only allow the NginX Docker Container write access to the directory only containing the HTTP Payload information. To minimise the attack surface of the HTTP endpoint.
Riding the wave: Handling HTTPS consistently
This solution focuses on a minimal HTTP upload endpoint, while HTTPS exposure is handled by an existing reverse proxy. In many cloud-native architectures, this approach reduces complexity by reusing existing secure layers. In our case we already have a Reverse Proxy available which can expose internal HTTP endpoints externally via HTTPS. So we reuse this proxy to not reinvent the wheel. NginX in general is perfectly capable of handling HTTPS requests, but we simply do not need it ourselves.
Closing thoughts
NginX has proven to be a reliable product for handling HTTPS traffic and it is perfectly capable to fulfil this task. With this Nginx Docker-based HTTP upload endpoint, our team benefits from extremely low maintenance overhead thanks to automated rebuilds in our DTAP process. Each rebuild integrates the latest security patches, ensuring the solution stays secure, reliable, and production-ready with minimal effort. These product rebuilds always use the latest security patches.