A New Collection of Thoughtful Learning Apps — Now Available on iOS & Android

Image
I’m excited to share a set of mobile apps I’ve recently completed and published on both the Google Play Store and the Apple App Store. These apps are designed with a simple goal in mind: to make meaningful, structured content more accessible, whether you’re studying theology or improving your English vocabulary. 📱 Now Available on Both Platforms All apps are live and available for download: Google Play Developer Page: https://play.google.com/store/apps/dev?id=5835943159853189043 Apple App Store Developer Page: https://apps.apple.com/ca/developer/q-z-l-corp/id1888794100 📖 Theology & Confession Study Apps For those interested in Reformed theology and classical Christian teachings, I’ve developed a series of apps that present foundational texts in a clean, focused reading format: The Belgic Confession Canons of Dort Heidelberg Catechism Westminster Shorter Catechism Each app is designed to provide a distraction-free experience, making it easier to read, reflect, and revisit these im...

Using Mutual TLS (mTLS) in Next.js (Server-Side Only)

Using Mutual TLS (mTLS) in Next.js (Server-Side Only)



In the previous posts, we covered:

Now we focus on Next.js applications and how mTLS works depending on deployment.


Next.js Cannot Access TLS Handshake Directly

  • Next.js middleware and API routes run after the TLS handshake
  • They cannot see client certificates or verify them
  • Next.js built-in server does not expose Node's HTTPS options like requestCert

In short: Next.js middleware cannot enforce mTLS or access TLS handshake details. Any enforcement must happen before the request reaches Next.js.


Next.js as an mTLS Client (Server-Side API Calls)

Next.js can securely call mTLS-protected APIs from server-side code, such as:

  • API routes
  • Server actions
import fs from 'fs';
import https from 'https';
import axios from 'axios';

export async function GET(req) {
  const httpsAgent = new https.Agent({
    key: fs.readFileSync('/certs/client.key'),
    cert: fs.readFileSync('/certs/client.crt'),
    ca: fs.readFileSync('/certs/ca.pem'),
    rejectUnauthorized: true,
  });

  try {
    const response = await axios.get('https://internal-api.example.com', {
      httpsAgent,
    });
    return new Response(JSON.stringify(response.data), { status: 200 });
  } catch (err) {
    console.error(err);
    return new Response('mTLS request failed', { status: 500 });
  }
}

This works both on Vercel and self-hosted deployments because it happens server-side.


Next.js as an mTLS Server

Next.js cannot enforce mTLS directly. How you handle mTLS depends on your deployment:

1. Vercel Deployment

  • You cannot run a custom HTTPS server on Vercel.
  • Next.js middleware and API routes cannot access TLS handshake or client certs.
  • Solution: Terminate mTLS at a reverse proxy or API gateway in front of Vercel.
  • The proxy validates client certificates, then forwards requests to Vercel.
  • Inject client info into headers (e.g., X-Client-CN) so Next.js can read it.

Example Nginx snippet:

server {
  listen 443 ssl;
  ssl_certificate /etc/certs/server.crt;
  ssl_certificate_key /etc/certs/server.key;
  ssl_client_certificate /etc/certs/ca.pem;
  ssl_verify_client on;

  location / {
    proxy_pass https://my-vercel-app.vercel.app;
    proxy_set_header X-Client-CN $ssl_client_s_dn;
  }
}

Next.js API route reading the header:

export async function GET(req) {
  const clientCN = req.headers.get('x-client-cn');
  return new Response(`Hello ${clientCN}`, { status: 200 });
}
---

2. Self-Hosted Deployment (Docker, VPS, Kubernetes, OpenShift)

  • You can run a custom Node HTTPS server wrapping Next.js.
  • Use requestCert: true and rejectUnauthorized: true to enforce client certs.
  • Forward requests to app.getRequestHandler().

Example:

import https from 'https';
import fs from 'fs';
import next from 'next';

const app = next({ dev: true });
const handle = app.getRequestHandler();

const server = https.createServer(
  {
    key: fs.readFileSync('/certs/server.key'),
    cert: fs.readFileSync('/certs/server.crt'),
    ca: fs.readFileSync('/certs/ca.pem'),
    requestCert: true,
    rejectUnauthorized: true,
  },
  (req, res) => handle(req, res)
);

server.listen(8443, () => console.log('Next.js mTLS server running on port 8443'));

This is only possible for **self-hosted deployments**. Platforms like Vercel do not allow custom HTTPS servers.


Summary: Vercel vs Self-Hosted

  • Vercel: mTLS must be terminated upstream (reverse proxy/API gateway). Next.js reads client info from headers.
  • Self-hosted: You can wrap Next.js in a custom HTTPS server and enforce client certificates directly.
  • Next.js middleware cannot enforce mTLS or access TLS handshake details in any deployment.
  • Server-side outgoing requests (mTLS client) work in both environments.

Using these patterns ensures your Next.js app participates in enterprise mTLS workflows securely, without exposing sensitive certificates.

❤️ Support This Blog


If this post helped you, you can support my writing with a small donation. Thank you for reading.


Comments

Popular Posts

Fix “A problem occurred starting process 'command node'” in Android Studio for React Native

Swagger annotations for API that allows downloading files as zip

2023, New Start! New AdSense Account! New Earnings!