First Understand — What Is the Difference Between HTTP and HTTPS?

❌ HTTP (HyperText Transfer Protocol)
  • Data travels in plain text
  • No encryption
  • Anyone on the network can see the data
  • Like a postcard—anyone who handles it can read it
✅ HTTPS (HTTP Secure)
  • Data is protected by TLS/SSL encryption
  • Everything travels encrypted
  • Even if intercepted on the network, it will not be readable
  • Like a sealed envelope—only the receiver can open it

🔑 A JWT token is sensitive login information—just like a password. And just as you wouldn't send a password in plain text, a JWT should not be transmitted over HTTP.

4 Real Risks of Sending JWT Over HTTP

1

Man-in-the-Middle (MitM) Attack

This is the biggest danger of sending JWT over HTTP.

In a man-in-the-middle attack, an attacker positions themselves between the client and the server and intercepts the network traffic.

This attack on HTTP goes something like this:

🔴 Over HTTP — Attack Succeeds
  1. The user sends a JWT token from their laptop to the server.
  2. An attacker on the same network (e.g., a coffee shop's WiFi) captures the traffic.
  3. The JWT token is visible in plain text—the attacker reads it.
  4. The attacker uses the same token to send requests to the server.
  5. The server understands that this is a legitimate user—and accepts the request.

This type of attack is very common on public Wi-Fi networks. Hotels, airports, cafés—the risk is particularly high in all these places.

🟢 Over HTTPS — Attack Fails
  1. User sends JWT token—but it's encrypted.
  2. Attacker captures traffic—but it all looks like gibberish.
  3. Token is unreadable without the decryption key.
  4. Attack fails.
2

Token Theft and Session Hijacking

Once the JWT token is intercepted over HTTP, the attacker can use it to hijack the entire session.

"Session hijacking" refers to a situation in which an attacker uses your valid token to impersonate you. The server believes that the request is coming from the real user, but in reality, it is coming from an attacker.

The consequences of this:

  • The attacker can carry out transactions through your account
  • Access your personal data
  • Change your settings
  • Send messages to other users on your behalf

The JWT token is stateless—the server has no session data to verify. Therefore, the server automatically trusts the token as soon as it receives a valid one.

3

Network Sniffing and Packet Analysis

With tools like Wireshark, one can analyze HTTP traffic on the same network.

HTTP Request Example (in Plain Text):

GET /api/user/profile HTTP/1.1 Host: yourapp.com Authorisation: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyMywicm9sZSI6ImFkbWluIn0.abc123

This entire request will be in plain text across the network. Using Wireshark, you can clearly see the JWT token in the Authorisation header.

This is what the request is encrypted with over HTTPS:

[Encrypted Data - TLSv1.3] [Application Data] ...unreadable gibberish...

A network sniffer will only see encrypted bytes — no readable information.

4

Proxy Server and Corporate Network Risks

Many companies and ISPs (Internet Service Providers) use proxy servers that log HTTP traffic.

If your application uses HTTP:

  • Corporate proxy servers can log every request—including JWT tokens
  • Monitoring at the ISP level is possible
  • Government surveillance is possible (in some countries)

HTTPS solves this problem as well—the proxy server can see the encrypted data, but not the content.

Relationship of JWT and HTTPS

A common misconception is that JWT itself is secure, so HTTPS isn't necessary. This is completely wrong.

JWT security and HTTPS security are two different layers:

🔏 JWT Security (Server-side)
  • Ensures that the token has not been tampered with
  • Verifies the signature
  • Proves that the token is authentic
🚛 HTTPS Security (Transport-level)
  • Ensures that the token is secure in transit
  • Will not be intercepted on the network
  • Protects against man-in-the-middle attacks

The two are complementary to each other – not substitutes.

💡 A simple analogy: A JWT is like a sealed letter; when you open it, you can tell if it has been tampered with. HTTPS is like an armored vehicle that delivers this letter securely. A sealed letter alone isn't enough—it also has to be delivered securely.

How to Implement HTTPS

Step 1

Get an SSL Certificate

Free Option — Let's Encrypt:

# Install Certbot (Ubuntu) sudo apt install certbot python3-certbot-nginx # Generate Certificate sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com # Setup auto-renewal sudo certbot renew --dry-run

Paid Options & Automatic HTTPS:

☁️ Cloudflare SSL Free tier available — easiest option
🔒 DigiCert / Comodo Paid — enterprise SSL options
▲ Vercel / Netlify Automatically enables HTTPS
🚂 Railway / Render Automatically HTTPS is enabled
☁️ AWS ELB + ACM Elastic Load Balancer + ACM Certificate
Step 2

Redirect HTTP to HTTPS

In Node.js/Express:

const express = require('express'); const app = express(); // HTTP to HTTPS redirect app.use((req, res, next) => { if (req.headers['x-forwarded-proto'] !== 'https' && process.env.NODE_ENV === 'production') { return res.redirect(301, 'https://' + req.headers.host + req.url); } next(); });

In Nginx configuration:

server { listen 80; server_name yourdomain.com www.yourdomain.com; # Redirect HTTP to HTTPS return 301 https://$server_name$request_uri; } server { listen 443 ssl; server_name yourdomain.com; ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; # JWT tokens will become secure }
Step 3

Add the HSTS Header

HSTS (HTTP Strict Transport Security) forces the browser to always use HTTPS.

// HSTS header in Express app.use((req, res, next) => { res.setHeader( 'Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload' ); next(); });

Or from Helmet.js (recommended):

const helmet = require('helmet'); app.use(helmet()); // HSTS is automatically set

Benefits of HSTS:

Users will not accidentally switch to HTTP
Browser will automatically use HTTPS
Protection against SSL stripping attacks
Step 4

Secure Flag — In Cookies

If storing the JWT token in cookies, be sure to set the secure flag:

res.cookie('accessToken', token, { httpOnly: true, secure: true, // will only be sent over HTTPS sameSite: 'Strict', maxAge: 15 * 60 * 1000 });

With the secure: true flag, the browser will never send this cookie over HTTP—no matter what the HTTP request is.

Step 5

Sending Token in Authorization Header

Is it safe to send a token in the Authorization header over HTTPS:

// Client side (Axios example) axios.defaults.baseURL = 'https://yourapi.com'; // always HTTPS const response = await axios.get('/api/profile', { headers: { 'Authorization': `Bearer ${accessToken}` } }); // Server side verification function authenticateToken(req, res, next) { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; if (!token) return res.status(401).json({ message: 'No token' }); try { const decoded = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'] }); req.user = decoded; next(); } catch (err) { res.status(403).json({ message: 'Invalid token' }); } }

Development vs. Production

🧪 Development (Local)
Using HTTP on localhost is generally acceptable because traffic doesn't go over the network. // Allow HTTP in Development if (process.env.NODE_ENV !== 'production') { // Local development over HTTP is okay }
🚀 Production (Live Server)
Not a single HTTP request is acceptable in Production. // Always enforce HTTPS in Production if (process.env.NODE_ENV === 'production') { app.use((req, res, next) => { if (req.protocol !== 'https') { return res.redirect(301, `https://${req.hostname}${req.url}`); } next(); }); }

Complete Security Checklist with HTTPS

Install an SSL/TLS certificate (Let's Encrypt — free)
301 redirect HTTP to HTTPS
Enable the HSTS header
Set the secure flag on the cookie
Keep the JWT secret key strong — generate it from jwtsecretkeygenerator.com
Set the token expiry (access token 15 min)
Keep the refresh token in an HTTP-only cookie
Specify the algorithm explicitly — algorithms: ['HS256']
Use TLS version 1.2 or 1.3 — disable older versions
Avoid mixed content — the page should not contain any HTTP resources

Mixed Content — A Hidden Risk

If your site is on HTTPS but a resource is being loaded via HTTP, this is called Mixed Content.

<!-- HTTP image on HTTPS page — False -->
<img src="http://example.com/image.jpg">
<!-- HTTPS API call — True -->
fetch('https://api.yourapp.com/data')

Mixed content allows attackers to access partial information. If you see Mixed Content warnings in the browser console, fix them.

Frequently Asked Questions

Why should JWT tokens be sent over HTTPS?

JWT tokens are sensitive authentication credentials. Sending them over HTTP means they travel in plain text and can be intercepted by anyone on the same network using tools like Wireshark. HTTPS encrypts the entire connection with TLS, making intercepted traffic unreadable. A JWT token sent over HTTP can be stolen and used to hijack the entire user session.

Is JWT itself enough for security without HTTPS?

No. JWT and HTTPS provide security at two different layers. JWT ensures the token hasn't been tampered with (integrity). HTTPS ensures the token is protected during transport (confidentiality). Both are complementary — not substitutes. Without HTTPS, even a perfectly signed JWT can be stolen in transit.

What is a man-in-the-middle attack on JWT?

In a man-in-the-middle attack over HTTP, an attacker on the same network (e.g., public Wi-Fi) intercepts the network traffic between the client and server, reads the JWT token in plain text, and then replays that token to the server to impersonate the legitimate user. HTTPS prevents this by encrypting the traffic so the token is unreadable even if intercepted.

How do I force HTTPS for JWT authentication in Node.js?

In Node.js/Express, check the x-forwarded-proto header and redirect HTTP to HTTPS in production. Add HSTS headers using Helmet.js. Set secure:true on JWT cookies so they are never sent over HTTP. Always use https:// base URLs in your API clients. Use Let's Encrypt (free) or a cloud platform like Vercel or Netlify that enables HTTPS automatically.