Single domain serve both a Next.js app and a WordPress site
To have a single domain (e.g., example.com) serve both a Next.js app and a WordPress site (e.g., as /admin, /cart, /checkout), you can use a reverse proxy configuration. Here’s how you can set it up:
1. File Structure
Assume your server has the following directories:
/var/www/
├── nextjs/ # Your Next.js app lives here
├── wordpress/ # WordPress site lives here
2. Use a Reverse Proxy
A reverse proxy server (e.g., Nginx or Apache) allows you to route requests to different folders or servers based on the URL path.
Nginx Example Configuration
Here’s how to configure Nginx for this setup:
server {
listen 80;
server_name example.com;
# Serve Next.js app for most routes
location / {
proxy_pass http://127.0.0.1:3000; # Next.js runs on port 3000
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
# Serve WordPress for specific routes (e.g., /admin, /cart, /checkout)
location /admin {
root /var/www/wordpress;
index index.php index.html;
try_files $uri $uri/ /index.php?$args;
}
location /cart {
root /var/www/wordpress;
index index.php index.html;
try_files $uri $uri/ /index.php?$args;
}
location /checkout {
root /var/www/wordpress;
index index.php index.html;
try_files $uri $uri/ /index.php?$args;
}
# Handle PHP scripts for WordPress
location ~ \.php$ {
root /var/www/wordpress;
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.1-fpm.sock; # Update PHP version if needed
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;
}
# Static file caching for WordPress
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|otf|webp)$ {
expires max;
log_not_found off;
}
}
Apache Example Configuration
For Apache, you would use mod_proxy and mod_rewrite:
<VirtualHost *:80>
ServerName example.com
# Serve Next.js app for most routes
ProxyPassMatch ^/(?!admin|cart|checkout) http://127.0.0.1:3000/
# Serve WordPress for specific routes
DocumentRoot /var/www/wordpress
<Directory /var/www/wordpress>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
RewriteEngine On
RewriteRule ^/(admin|cart|checkout)(.*)$ /$1$2 [L]
</VirtualHost>
3. Next.js Configuration
Ensure your Next.js app runs on a specific port (e.g., 3000) and listens to the reverse proxy.
Update next.config.js if required:
module.exports = {
trailingSlash: false,
basePath: '',
};
4. WordPress Configuration
Update wp-config.php:
In wp-config.php, you need to ensure WordPress recognizes the correct site URL for paths like /admin, /cart, and /checkout:
define('WP_HOME', 'http://example.com');
define('WP_SITEURL', 'http://example.com');
Ensure permalinks are configured correctly in WordPress Settings > Permalinks to work with the reverse proxy setup.
5. Shared Sessions Across Apps
Now that the domain is shared, you need to handle sessions and cookies:
A. Use Domain-Wide Cookies
- Set cookies with
Domain=.example.com. - Example in Next.js (via API routes or middleware):
res.setHeader('Set-Cookie', `authToken=${token}; Path=/; Domain=.example.com; HttpOnly`);
B. Shared Session Storage
- Use a database like Redis for shared session data.
- Example Node.js session store:
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const redisClient = require('redis').createClient();
app.use(
session({
store: new RedisStore({ client: redisClient }),
secret: 'your-secret',
resave: false,
saveUninitialized: false,
cookie: { domain: '.example.com' },
})
);
To maintain sessions between a Next.js app and WordPress, you need to centralize the session management. Here’s a step-by-step breakdown to achieve this:
The session can be maintained across both platforms by centralizing it in one of the following ways:
- JWT-based authentication: Generate a token on login and validate it on both Next.js and WordPress.
- Shared session storage: Store session data in a shared database or a caching layer like Redis or Memcached.
- Domain-wide cookies: Use cookies that are accessible across both Next.js and WordPress since they share the same domain.
Option A: Using JWT for Authentication
- Authenticate Users
- User logs in via WordPress or Next.js.
- Generate a JWT (JSON Web Token) that contains user data (e.g., user ID, roles).
const jwt = require('jsonwebtoken');
const generateToken = (user) => {
return jwt.sign({ id: user.id, email: user.email }, 'your-secret-key', {
expiresIn: '1h', // Token expiration
});
};
On successful login, return the token to the client and store it as a cookie or in localStorage.
Verify JWT
- Both Next.js and WordPress can verify the same token using the shared secret key.
Example in Node.js:
const verifyToken = (token) => {
try {
return jwt.verify(token, 'your-secret-key');
} catch (err) {
return null; // Invalid token
}
};
Example in PHP (WordPress): Use the JWT Auth Plugin.
require_once 'vendor/autoload.php';
use Firebase\JWT\JWT;
$decoded = JWT::decode($token, 'your-secret-key', ['HS256']);
Set Domain-Wide Cookies When generating the JWT, set it as a cookie accessible across the entire domain (example.com):
res.setHeader('Set-Cookie', `authToken=${token}; Path=/; Domain=.example.com; HttpOnly; Secure`);
Option B: Using a Shared Session Storage
- Setup a Shared Session Store Use Redis, Memcached, or even your MySQL database to store session data.
- Store Session in Redis Example using Redis for session storage in Node.js:
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const redisClient = require('redis').createClient();
app.use(
session({
store: new RedisStore({ client: redisClient }),
secret: 'your-secret-key',
resave: false,
saveUninitialized: false,
cookie: { domain: '.example.com', secure: true },
})
);
Access Session in WordPress In WordPress, use a plugin like Redis Object Cache to connect to the same Redis server.
Example custom PHP function for retrieving session data:
function get_user_session($session_id) {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379); // Connect to Redis
return $redis->get($session_id); // Retrieve session data
}
Synchronize Session IDs Share the session ID via a cookie. For example:
res.setHeader('Set-Cookie', `sessionId=${sessionId}; Path=/; Domain=.example.com; HttpOnly; Secure`);
Option C: Centralized User Authentication via WordPress REST API
If WordPress is the main backend:
- Enable WordPress REST API Use WordPress’s REST API to authenticate users. Install a plugin like JWT Authentication for WP REST API.
- Authenticate via REST API In Next.js, authenticate users by sending a request to WordPress’s
/wp-json/jwt-auth/v1/tokenendpoint:
const login = async (username, password) => {
const response = await fetch('https://example.com/wp-json/jwt-auth/v1/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
});
const data = await response.json();
if (data.token) {
document.cookie = `authToken=${data.token}; Path=/; Domain=.example.com; HttpOnly; Secure`;
}
};
Validate Tokens in WordPress WordPress can validate the same token for cart and checkout.
Next.js Middleware for Protected Pages Add a middleware in Next.js to validate the session for protected pages:
import jwt from 'jsonwebtoken';
export default async function middleware(req, res, next) {
const token = req.cookies.authToken;
if (token) {
try {
const decoded = jwt.verify(token, 'your-secret-key');
req.user = decoded;
} catch (err) {
res.status(401).json({ message: 'Unauthorized' });
}
} else {
res.status(401).json({ message: 'Unauthorized' });
}
next();
}
6. Workflow for Requests
- User visits the site:
- Routes like
/or/aboutare handled by Next.js. - Routes like
/admin,/cart, or/checkoutare proxied to WordPress.
- Routes like
- Session Sharing:
- When a user logs in via WordPress or Next.js, the session is stored in shared storage (e.g., Redis).
- Both systems can validate the session using the same token/cookie.
- Seamless Integration:
- The user doesn’t notice the switch between Next.js and WordPress because everything is served under
example.com.
- The user doesn’t notice the switch between Next.js and WordPress because everything is served under