- Project Overview
- What is a Reverse Proxy?
- System Architecture
- Prerequisites
- Implementation Steps
- Key Configuration Elements Explained
- Lessons Learned
- Future Improvements
- Troubleshooting
- Repository Structure
- License
- Contact Information
This project demonstrates the implementation of an NGINX reverse proxy server on AWS to route traffic to multiple backend services. The reverse proxy provides a unified access point for two different applications:
- A Python application running on port 8080
- A Node.js application running on port 3000
By using NGINX as a reverse proxy, we can access both applications through a single IP address while keeping the actual service ports secured from public access.
A reverse proxy is a server that sits between clients and backend application servers. It forwards client requests to appropriate backend servers and returns the server's responses to the clients, appearing as the origin server to clients.
- Security: Hide backend services and ports from public access
- Unified Access: Access multiple applications through a single entry point
- Path-Based Routing: Direct traffic to different applications based on URL paths
- Simplified Client Experience: Clients don't need to remember different ports for different services
If the diagram doesn't render properly, here's a text representation of the architecture:
- AWS account with EC2 permissions
- Basic knowledge of Linux commands
- Understanding of web servers and networking concepts
- SSH client for connecting to the EC2 instance
- Launch an EC2 instance with Ubuntu Server
- Clone this repository:
git clone https://github.com/matthewntsiful/nginx-reverse-proxy-aws.git
- Run the setup script:
cd nginx-reverse-proxy-aws && bash scripts/setup.sh
- Access the applications at:
- Python app: http://your-server-ip/pythonapp/
- Node.js app: http://your-server-ip/nodejsapp/
- Launched an EC2 instance with Ubuntu Server
- Configured Security Group to allow:
- SSH (Port 22) for administration
- HTTP (Port 80) for web traffic
- Initially allowed ports 8080 and 3000 for testing direct access
Created two backend services in separate directories:
mkdir service1
# Start Python server on port 8080
# Simple HTTP server for demonstration
mkdir service2
# Start Node.js server on port 3000
# Express application for demonstration
Tested direct access to both services through their respective ports to ensure they were functioning correctly:
- Python app: http://your-server-ip:8080
- Node.js app: http://your-server-ip:3000
Secured the backend services by blocking direct access to ports 8080 and 3000 using UFW (Uncomplicated Firewall):
sudo ufw deny 8080
sudo ufw deny 3000
sudo ufw allow 80
sudo ufw status
Verified that direct access to the services was no longer possible:
sudo apt update
sudo apt install nginx
Created a custom configuration file in the sites-available directory:
sudo nano /etc/nginx/sites-available/reverse-proxy
Added the following configuration:
# Main server block configuration
server {
# Listen on port 80 for HTTP traffic
listen 80;
# Set the server name to the IP address
server_name your-server-ip;
# Configuration block for Python application
# Handles all requests under /pythonapp/ path
location /pythonapp/ {
# Forward requests to Python application running on localhost port 8080
proxy_pass http://127.0.0.1:8080/; # Added trailing slash
# Standard proxy headers section
# Pass the original host header to the backend
proxy_set_header Host $host;
# Pass the real client IP address
proxy_set_header X-Real-IP $remote_addr;
# Pass the proxy chain information
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Pass the original protocol (http/https)
proxy_set_header X-Forwarded-Proto $scheme;
# Performance optimization settings
# Enable proxy buffering for better performance
proxy_buffering on;
# Set the size of the proxy buffer
proxy_buffer_size 4k;
# Configure number and size of proxy buffers
proxy_buffers 8 16k;
# Set timeout for establishing connection
proxy_connect_timeout 60s;
# Set timeout for sending data
proxy_send_timeout 60s;
# Set timeout for reading response
proxy_read_timeout 60s;
}
# Configuration block for Node.js application
# Handles all requests under /nodejsapp/ path
location /nodejsapp/ {
# Forward requests to Node.js application running on localhost port 3000
proxy_pass http://127.0.0.1:3000/; # Trailing slash already present
# Standard proxy headers section
# Pass the original host header to the backend
proxy_set_header Host $host;
# Pass the real client IP address
proxy_set_header X-Real-IP $remote_addr;
# Pass the proxy chain information
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Pass the original protocol (http/https)
proxy_set_header X-Forwarded-Proto $scheme;
# Performance optimization settings
# Enable proxy buffering for better performance
proxy_buffering on;
# Set the size of the proxy buffer
proxy_buffer_size 4k;
# Configure number and size of proxy buffers
proxy_buffers 8 16k;
# Set timeout for establishing connection
proxy_connect_timeout 60s;
# Set timeout for sending data
proxy_send_timeout 60s;
# Set timeout for reading response
proxy_read_timeout 60s;
}
# Define custom error pages for server errors
error_page 500 502 503 504 /50x.html;
# Security header configurations
# Prevent site from being embedded in iframes on other domains
add_header X-Frame-Options "SAMEORIGIN";
# Prevent MIME-type sniffing
add_header X-Content-Type-Options "nosniff";
# Enable browser's XSS protection
add_header X-XSS-Protection "1; mode=block";
}
Linked the configuration to the sites-enabled directory and tested the configuration:
sudo ln -s /etc/nginx/sites-available/reverse-proxy /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
Verified that both applications were accessible through the NGINX reverse proxy:
- Python application: http://your-server-ip/pythonapp/
- Node.js application: http://your-server-ip/nodejsapp/
The configuration uses location blocks to route requests based on the URL path:
- Requests to
/pythonapp/
are routed to the Python application - Requests to
/nodejsapp/
are routed to the Node.js application
The proxy_set_header
directives ensure that important information is passed to the backend servers:
Host
: Preserves the original host headerX-Real-IP
: Forwards the client's IP addressX-Forwarded-For
: Maintains the chain of IP addressesX-Forwarded-Proto
: Indicates the original protocol (HTTP/HTTPS)
Several settings improve proxy performance:
proxy_buffering
: Enables buffering of responsesproxy_buffer_size
andproxy_buffers
: Configure buffer sizes- Timeout settings: Control connection, sending, and reading timeouts
Added security headers to protect against common web vulnerabilities:
X-Frame-Options
: Prevents clickjacking attacksX-Content-Type-Options
: Prevents MIME-type sniffingX-XSS-Protection
: Enables browser's built-in XSS protection
Throughout this project, I gained hands-on experience with:
- NGINX Configuration: Understanding the syntax and structure of NGINX configuration files
- Reverse Proxy Concepts: Implementing path-based routing and header forwarding
- Server Security: Using firewalls to restrict direct access to backend services
- AWS Infrastructure: Deploying and configuring services on EC2 instances
Possible enhancements for this project:
- SSL/TLS Implementation: Adding HTTPS support with Let's Encrypt
- Load Balancing: Expanding to multiple backend instances for each service
- Monitoring: Implementing NGINX status monitoring
- Caching: Adding caching for static content
- Automated Deployment: Creating scripts for automated setup and configuration
- Check if backend services are running:
ps aux | grep python
andps aux | grep node
- Verify NGINX configuration:
sudo nginx -t
- Check NGINX error logs:
sudo tail -f /var/log/nginx/error.log
- Verify firewall settings:
sudo ufw status
- Check if services are listening on correct ports:
sudo netstat -tulpn | grep LISTEN
/
├── README.md # This documentation file
├── imgs/ # Directory for all images
│ ├── architecture/ # System architecture diagrams
│ │ └── nginx-reverse-proxy.png
│ └── testing/ # Testing screenshots
│ ├── direct-access/
│ │ ├── nodejs-access.png
│ │ └── python-access.png
│ ├── blocked-ports/
│ │ ├── firewall-blocked-nodejs.png
│ │ └── firewall-blocked-python.png
│ └── nginx-access/
│ ├── nginx-access.png
│ ├── nodejsapp-service.png
│ └── pythonapp-service.png
├── nginx/ # NGINX configuration files
│ └── reverse_proxy.conf
├── apps/
│ ├── python-app/ # Python application code
│ └── nodejs-app/ # Node.js application code
└── scripts/
└── setup.sh # Setup script for automated deployment
This project demonstrates a practical implementation of an NGINX reverse proxy on AWS, providing a secure and unified way to access multiple backend services. By hiding the backend services behind the reverse proxy and implementing proper routing, we've created a more secure and user-friendly system.
The implementation showcases how to effectively use NGINX to manage traffic between clients and multiple backend applications, while also applying security best practices to protect the underlying services.
This project is licensed under the MIT License - see the LICENSE file for details.
For questions or contributions, please contact:
- GitHub: matthewntsiful
- Email: [email protected]