Skip to content
-
Subscribe to our newsletter & never miss our best posts. Subscribe Now!
Devnix Blog

Tech Trends, Software Engineering & Cloud Insights

Devnix Blog

Tech Trends, Software Engineering & Cloud Insights

  • Home
  • Privacy Policy
  • Home
  • Privacy Policy
Close

Search

Subscribe
Server Security

Field Notes: Hardening Nginx TLS on a Cloud VPS for a Public API

By Devnix
June 17, 2026 4 Min Read
0


Field Notes: Hardening Nginx TLS on a Cloud VPS for a Public API

When a small SaaS startup exposed its JSON API to the internet, the first wave of customers reported intermittent SSL handshake failures and, more worryingly, a few security‑scan alerts flagging weak ciphers. The engineering team needed a quick, reproducible hardening process that would keep the API reachable, comply with industry best‑practice TLS settings, and stay within the modest resources of a single‑CPU Cloud VPS.

Problem Statement

The API runs on a Ubuntu 22.04 LTS droplet with nginx acting as a reverse proxy to a Node.js service listening on localhost:3000. Initial Nginx configuration used the default ssl_protocols and ssl_ciphers directives, which left the server vulnerable to:

  • TLS 1.0/1.1 support (deprecated).
  • Cipher suites susceptible to MITM attacks (e.g., RC4, 3DES).
  • Missing HTTP Strict Transport Security (HSTS) header.
  • No OCSP stapling, increasing latency for certificate validation.

Complicating factors:

  • The VPS provides only 1 vCPU and 1 GB RAM – heavy‑weight solutions must stay lightweight.
  • Zero‑downtime is mandatory; the API cannot be taken offline for a prolonged period.
  • Compliance requirements demand at least TLS 1.2 with forward‑secrecy.

Constraints and Assumptions

  • We assume a valid Let’s Encrypt certificate is already issued.
  • All traffic must be forced over HTTPS; HTTP must redirect to HTTPS.
  • Only standard Ubuntu repositories may be used – no third‑party binaries that could increase attack surface.
  • Monitoring and alerting are already in place via systemd and netdata, so we only need to add health checks for TLS.

Technical Decisions

After evaluating common hardening patterns, the team settled on the following:

  • Use nginx built‑in TLS features – no external reverse‑proxy or load balancer.
  • Enable only TLS 1.2 and TLS 1.3, disabling older protocols.
  • Adopt the Mozilla “Intermediate” cipher suite recommendation, which balances security and compatibility.
  • Add HSTS with a 6‑month max‑age and include subdomains.
  • Enable OCSP stapling to reduce client latency.
  • Leverage systemd socket activation for graceful reloads, ensuring zero‑downtime.

To host the API reliably, the team provisioned a Cloud VPS with 1 vCPU, 1 GB RAM, and 25 GB SSD. This configuration offers enough headroom for Nginx, the Node.js process, and the occasional burst of traffic without incurring unnecessary cost.

Implementation Steps

1. Install and Verify Nginx

sudo apt update
sudo apt install -y nginx
nginx -v   # should report 1.18.0+ (or newer) with TLS support

2. Obtain a Let’s Encrypt Certificate (if not already present)

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d api.example.com

Certbot automatically configures a basic HTTPS server block. We will replace it with a hardened version.

3. Harden the Nginx Server Block

Create a dedicated file /etc/nginx/sites-available/api.conf and link it:

sudo nano /etc/nginx/sites-available/api.conf
sudo ln -s /etc/nginx/sites-available/api.conf /etc/nginx/sites-enabled/

Insert the following configuration (comments explain each directive):

server {
    listen 80;
    server_name api.example.com;
    return 301 https://$host$request_uri;  # HTTP → HTTPS redirect
}

server {
    listen 443 ssl http2;
    server_name api.example.com;

    # TLS protocol selection
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;

    # Mozilla Intermediate cipher suite
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:
                 ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:
                 ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';

    # Forward secrecy & perfect forward secrecy
    ssl_ecdh_curve X25519:secp384r1;

    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    # HSTS (6 months, include subdomains)
    add_header Strict-Transport-Security "max-age=15768000; includeSubDomains" always;

    # Additional security headers
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options SAMEORIGIN;
    add_header Referrer-Policy no-referrer-when-downgrade;

    # TLS session settings
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;

    # Root and proxy settings
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

4. Test Configuration and Reload Gracefully

sudo nginx -t   # syntax check
sudo systemctl reload nginx   # reload without dropping connections

5. Verify TLS Settings

Use openssl and sslscan to confirm the server only offers TLS 1.2/1.3 and the expected cipher list.

openssl s_client -connect api.example.com:443 -tls1_2 -servername api.example.com 
sslscan --no-fallback --no-renegotiation api.example.com

6. Automate Certificate Renewal

Certbot installs a systemd timer by default, but we add a sanity check to reload Nginx after renewal.

sudo systemctl edit certbot-renew.service

# Add the following override:
[Service]
ExecPost=/usr/sbin/nginx -s reload

Risks & Mitigation Strategies

  • Client Compatibility: Some legacy clients may only support TLS 1.0/1.1. Mitigation – keep a separate sub‑domain for internal tools, or log failed handshakes and inform users.
  • OCSP Stapling Failures: If the resolver cannot reach the OCSP responder, connections may stall. Mitigation – configure multiple public DNS resolvers and monitor nginx error logs for ssl_stapling errors.
  • Configuration Drift: Manual edits could revert hardening. Mitigation – store /etc/nginx in Git, enforce pull‑request reviews, and use ansible or chef for idempotent deployment.
  • Resource Exhaustion: TLS handshakes are CPU‑intensive. Mitigation – enable ssl_session_cache and monitor CPU usage; if needed, upgrade to a higher‑tier VPS.

Operational Checks

After deployment, the team added the following recurring checks to their monitoring suite:

  • Certificate Expiry Alert: Use certbot certificates output parsed by cron to trigger an email 30 days before expiry.
  • Handshake Success Rate: Netdata’s nginx.ssl_handshake metric should stay above 99.9 %.
  • OCSP Stapling Health: Log pattern "ssl_stapling error" and create a Grafana alert if occurrences exceed a threshold.
  • HSTS Header Verification: Periodically curl the endpoint and grep for Strict-Transport-Security.

Outcome

Within a single maintenance window, the API achieved:

  • Full TLS 1.2/1.3 compliance, passing the SSL Labs “A” rating.
  • Zero‑downtime reloads thanks to systemd‑driven Nginx reloads.
  • Reduced latency for TLS handshakes (average 45 ms) after enabling OCSP stapling.
  • No further security‑scan warnings in the next quarterly audit.

The lightweight Cloud VPS proved sufficient for the hardened configuration, and the team now uses the same hardening template for all new micro‑services.

Conclusion

Hardening Nginx TLS on a modest Cloud VPS can be accomplished with a handful of well‑documented directives, careful testing, and automated renewal hooks. By treating the TLS configuration as code, the team minimized drift, ensured compliance, and kept the public API secure without sacrificing performance or availability.

Tags:

cloud VPS securitynginx TLS hardeningssl configuration
Author

Devnix

Follow Me
Other Articles
Previous

Migrating a Growing WordPress Site from Shared Hosting to a Cloud VPS: A Real‑World Scenario

Next

Why Your Linux Server Is Getting Locked Out: Symptoms, Causes, Diagnostics, and Recovery

No Comment! Be the first one.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recent Posts

  • How a Growing Marketing Agency Decided Between WordPress, Static, and Cloud VPS Hosting
  • Common Mistakes That Sabotage WordPress Caching and CDN Performance
  • Why Your WordPress Cron Jobs Stall and How to Get Them Running Again
  • Why Server Log Monitoring Is the First Line of Defense Against Outages
  • Choosing the Right Approach for Purchase Order Approval in Odoo

Archives

  • July 2026
  • June 2026
  • May 2026

Categories

  • Backup Strategies
  • Cloud VPS Performance
  • Container Orchestration
  • Docker Compose Deployment
  • Hosting Solutions
  • Immutable Infrastructure
  • Odoo Email Configuration
  • Odoo Inventory
  • Odoo Invoicing
  • Odoo Multi-Company Configuration
  • Odoo Subscriptions
  • Odoo User Management
  • Server Security
  • WordPress Maintenance Mode
  • WordPress Migration
  • WordPress Performance Optimization

About Devnix Blog

A forward-thinking tech publication covering software engineering, cloud infrastructure, and modern digital transformation. Built for developers and tech enthusiasts.

Our Services

  • Cloud VPS Hosting
  • Managed ERP Solutions
  • DevOps Automation
  • Server Security & Optimization

Partners

  • Odoo Stack
  • Odoo Backup
  • Devnix Solutions
Copyright 2026 — Devnix Blog. All rights reserved. Devnix Solutions