1. Disclaimer
In writing this article, I aim to provide technical guidance and examples for tech enthusiasts on how to set up a reverse proxy using Netlify. Please note that the information and advice in this article are for reference only, and specific implementations may vary based on actual circumstances. When using the methods and steps provided in this article, please ensure compliance with local laws and regulations, and fully consider the requirements for network security and data protection.
The author of this article and the publishing platform bear no responsibility for any direct or indirect losses, damages, or legal liabilities arising from the use of the content of this article. It is recommended to conduct thorough testing and risk assessment before implementing any technical solutions and to seek professional advice to ensure compliance with applicable laws and industry standards.
2. Legal Use Statement
Please comply with relevant laws and regulations, including but not limited to the Cybersecurity Law of the People's Republic of China and the Internet Information Service Management Measures. Ensure that when using Netlify or other services:
- Legal Compliance: Ensure that the reverse proxy set up is not used for activities that violate laws and regulations, including but not limited to bypassing censorship, illegal data transmission, or other unlawful purposes.
- Respect Copyright and Privacy: Comply with copyright laws and ensure that the content being proxied does not infringe on third-party intellectual property rights. At the same time, protect user privacy and follow relevant data protection requirements.
- Network Security: Take necessary security measures to prevent the reverse proxy service from being used for malicious purposes, ensuring the security of systems and user data.
- Information Transparency: Ensure that the service information provided is clear and transparent, allowing users to clearly understand the purpose of the reverse proxy and potential risks.
Introduction/Abstract#
In today's internet environment, many users are troubled by the inability to access certain foreign and domestic websites due to DNS pollution and other reasons. Additionally, some websites can only be accessed via IP addresses and cannot bind custom domain names, which also limits their flexibility and usability. To solve these problems, setting up a reverse proxy server has become an effective solution. A reverse proxy can help users bypass DNS pollution issues and allow us to access websites that can only be accessed via IP addresses using a custom domain name.
In this article, we will use Netlify, a powerful and easy-to-use tool, to set up a reverse proxy. The strength of Netlify lies in its simple configuration process and powerful features, making it easy for even users who are not very familiar with technology to get started. To complete this task, you only need two basic requirements: a Netlify account and a GitHub account. Next, we will detail how to configure the reverse proxy using these two accounts.
Main Content#
Friendly Reminder
The content marked with (optional) after the title is related to the backend. If not needed, you can skip it.
Step 1 Prepare the Environment for Setting Up the Reverse Proxy#
1. Create a Repository#
Create a GitHub repository to store the Netlify configuration files.
As shown in the image:
2. Create a Configuration File#
Create a new configuration file named netlify.toml
.
As shown in the image:
The content of the file is:
[[redirects]]
from = "/*"
to = "https://github.com/:splat"
status = 200
Optional Content#
Create another repository for setting up the management backend (optional).
As shown in the image:
Files Included in the Repository (optional):#
│ index.html
│ package.json
│ README.md
│
└─netlify
└─functions
auth-callback.js
check-auth.js
update-redirect.js
- index.html: The main front-end page of the application, containing the user login and reverse proxy configuration form. (optional)
Friendly Reminder
Please fill in your reverse proxy website (not the backend) in the code where it says 【here fill in your reverse proxy website】 and fill in your GitHub client ID in 【here fill in your GitHub clientid】. See below for how to obtain it.
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Reverse Proxy Configuration</title>
<style>
body {
font-family: Arial, sans-serif;
background: linear-gradient(to bottom, #87CEFA, #FFFFFF);
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
overflow-x: hidden;
}
.container {
text-align: center;
width: 90%;
max-width: 500px;
}
h1 {
color: #333;
margin-bottom: 20px;
font-size: 1.8em;
}
form {
background: #f9f9f9;
border-radius: 15px;
padding: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease-in-out;
}
form:hover {
transform: scale(1.02);
}
label {
font-size: 1.2em;
display: block;
margin-bottom: 10px;
color: #555;
}
input[type="url"] {
width: calc(100% - 22px);
padding: 10px;
border-radius: 8px;
border: 1px solid #ddd;
margin-bottom: 20px;
font-size: 1em;
box-sizing: border-box;
}
button {
background-color: #87CEFA;
color: white;
border: none;
padding: 10px 20px;
border-radius: 8px;
cursor: pointer;
font-size: 1.1em;
transition: background-color 0.3s ease-in-out;
}
button:hover {
background-color: #00BFFF;
}
#message {
font-size: 1.2em;
color: #333;
margin-top: 20px;
}
.error-container,
.success-container {
display: none;
background: #f0f8ff;
border: 1px solid #d4edda;
border-radius: 8px;
padding: 20px;
margin-top: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
text-align: center;
}
.error-container {
border-color: #f5c6cb;
background: #f8d7da;
color: #721c24;
}
.success-container {
border-color: #c3e6cb;
background: #d4edda;
color: #155724;
}
.error-container p,
.success-container p {
margin: 0;
}
.success-container button {
margin-top: 10px;
background-color: #4CAF50;
border: none;
}
.success-container button:hover {
background-color: #45a049;
}
.tooltip {
position: relative;
display: inline-block;
cursor: pointer;
}
.tooltip .tooltiptext {
visibility: hidden;
width: 200px;
background-color: #333;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 10px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -100px;
opacity: 0;
transition: opacity 0.3s;
}
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
#loginContainer {
display: none;
}
</style>
</head>
<body>
<div class="container">
<h1>Reverse Proxy Configurator</h1>
<div id="loginContainer">
<p>Please log in to access this page.</p>
<button id="loginButton">Log in with GitHub</button>
</div>
<form id="redirectForm" style="display: none;">
<div class="tooltip">
<label for="website">Please enter the URL to reverse proxy to:</label>
<span class="tooltiptext">Please enter the full URL, e.g., https://example.com</span>
</div>
<input type="url" id="website" name="website" required>
<button type="submit">Submit</button>
</form>
<div id="errorMessage" class="error-container">
<p id="errorStatusMessage"></p>
</div>
<div id="successMessage" class="success-container">
<p id="successStatusMessage"></p>
<button id="redirectButton" style="display: none;">Go to Reverse Proxy Website</button>
</div>
</div>
<script>
async function checkAuthentication() {
const response = await fetch('/.netlify/functions/check-auth');
const result = await response.json();
if (result.authenticated) {
document.getElementById('loginContainer').style.display = 'none';
document.getElementById('redirectForm').style.display = 'block';
} else {
document.getElementById('loginContainer').style.display = 'block';
document.getElementById('redirectForm').style.display = 'none';
}
}
document.getElementById('loginButton').addEventListener('click', () => {
window.location.href = `https://github.com/login/oauth/authorize?client_id=【here fill in your GitHub clientid】&redirect_uri=${encodeURIComponent(window.location.origin + '/.netlify/functions/auth-callback')}`;
});
document.getElementById('redirectForm').addEventListener('submit', async (event) => {
event.preventDefault();
const website = document.getElementById('website').value;
const urlPattern = /^https:\/\/[^\/]+$/;
const errorMessageElement = document.getElementById('errorStatusMessage');
const successMessageElement = document.getElementById('successStatusMessage');
const errorContainer = document.getElementById('errorMessage');
const successContainer = document.getElementById('successMessage');
const redirectButton = document.getElementById('redirectButton');
if (!urlPattern.test(website)) {
const suggestion = website.replace(/\/+$/, '');
errorMessageElement.innerText = `The URL format entered is incorrect. Please ensure the website is entered in the form 'https://example.com'. A possible correct input is: ${suggestion}`;
errorContainer.style.display = 'block';
successContainer.style.display = 'none';
return;
}
const response = await fetch('/.netlify/functions/update-redirect', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ website })
});
const result = await response.json();
if (result.message === 'Redirect updated successfully!') {
successMessageElement.innerText = 'Reverse proxy configuration successful! Please wait for the server to update, estimated 1 minute.';
successContainer.style.display = 'block';
errorContainer.style.display = 'none';
redirectButton.style.display = 'inline-block';
redirectButton.addEventListener('click', () => {
window.location.href = '【here fill in your reverse proxy website, not the backend】';
});
} else {
errorMessageElement.innerText = `Configuration failed: ${result.message}`;
errorContainer.style.display = 'block';
successContainer.style.display = 'none';
}
});
// Initialize the authentication check
checkAuthentication();
</script>
</body>
</html>
- /netlify/functions/auth-callback.js: Function to handle GitHub OAuth callback and set the user's login status. (optional)
exports.handler = async function(event, context) {
const clientId = process.env.GITHUB_CLIENT_ID;
const clientSecret = process.env.GITHUB_CLIENT_SECRET;
const allowedUsers = process.env.ALLOWED_USERS.split(',');
const jwtSecret = process.env.JWT_SECRET;
const code = event.queryStringParameters.code;
if (!code) {
return {
statusCode: 400,
body: JSON.stringify({ message: 'Missing code parameter' })
};
}
try {
const fetch = (await import('node-fetch')).default;
const jwt = (await import('jsonwebtoken')).default;
const response = await fetch(`https://github.com/login/oauth/access_token`, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
client_id: clientId,
client_secret: clientSecret,
code: code
})
});
const data = await response.json();
if (data.error) {
throw new Error(data.error_description);
}
const userResponse = await fetch('https://api.github.com/user', {
headers: {
Authorization: `token ${data.access_token}`
}
});
const userInfo = await userResponse.json();
if (allowedUsers.includes(userInfo.login)) {
const token = jwt.sign({ username: userInfo.login }, jwtSecret, { expiresIn: '1h' });
return {
statusCode: 302,
headers: {
'Set-Cookie': `token=${token}; Path=/; HttpOnly`,
'Location': '/'
},
body: ''
};
} else {
return {
statusCode: 403,
body: JSON.stringify({ message: 'Unauthorized' })
};
}
} catch (error) {
return {
statusCode: 500,
body: `Error: ${error.message}`
};
}
};
- /netlify/functions/check-auth.js: Function to check if the user is logged in. (optional)
exports.handler = async function(event, context) {
const jwtSecret = process.env.JWT_SECRET;
const cookieHeader = event.headers.cookie || '';
const cookies = Object.fromEntries(cookieHeader.split('; ').map(cookie => cookie.split('=')));
const token = cookies.token;
try {
const jwt = (await import('jsonwebtoken')).default;
jwt.verify(token, jwtSecret);
return {
statusCode: 200,
body: JSON.stringify({ authenticated: true })
};
} catch (err) {
return {
statusCode: 403,
body: JSON.stringify({ authenticated: false })
};
}
};
- /netlify/functions/update-redirect.js: Function to handle reverse proxy configuration. (optional)
Friendly Reminder
Please fill in your GitHub username and the name of the GitHub repository used to store the Netlify configuration files in the code where it says 【here fill in your GitHub username】【name of the GitHub repository used to store the Netlify configuration files】. See below for how to obtain it.
exports.handler
const jwtSecret = process.env.JWT_SECRET;
const allowedUsers = process.env.ALLOWED_USERS.split(',');
const cookieHeader = event.headers.cookie || '';
const cookies = Object.fromEntries(cookieHeader.split('; ').map(cookie => cookie.split('=')));
const token = cookies.token;
let decodedToken;
try {
const jwt = (await import('jsonwebtoken')).default;
decodedToken = jwt.verify(token, jwtSecret);
} catch (err) {
return {
statusCode: 403,
body: JSON.stringify({ message: 'Unauthorized' })
};
}
if (!allowedUsers.includes(decodedToken.username)) {
return {
statusCode: 403,
body: JSON.stringify({ message: 'Unauthorized' })
};
}
const { Octokit } = await import('@octokit/rest');
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN
});
const { website } = JSON.parse(event.body);
if (!website) {
return {
statusCode: 400,
body: JSON.stringify({ message: 'No website provided' })
};
}
try {
const fetch = (await import('node-fetch')).default;
const { data: fileData } = await octokit.repos.getContent({
owner: '【here fill in your GitHub username】',
repo: '【name of the GitHub repository used to store the Netlify configuration files】',
path: 'netlify.toml'
});
const content = Buffer.from(fileData.content, 'base64').toString();
const newContent = content.replace(/to\s*=\s*"[^"]+"/, `to = "${website}/:splat"`);
await octokit.repos.createOrUpdateFileContents({
owner: '【here fill in your GitHub username】',
repo: '【name of the GitHub repository used to store the Netlify configuration files】',
path: 'netlify.toml',
message: 'Update redirect URL',
content: Buffer.from(newContent).toString('base64'),
sha: fileData.sha
});
return {
statusCode: 200,
body: JSON.stringify({ message: 'Redirect updated successfully!' })
};
} catch (error) {
console.error(error);
return {
statusCode: 500,
body: JSON.stringify({ message: 'Failed to update redirect' })
};
}
};
- package.json: File to store dependencies. (optional)
{ "name": "Rcs, "version": "1.0.0", "description": "A reverse proxy configurator with GitHub authentication deployed on Netlify.", "main": "index.js", "scripts": { "start": "netlify dev", "build": "netlify build" }, "dependencies": { "@octokit/rest": "^19.0.7", "jsonwebtoken": "^9.0.2", "node-fetch": "^3.3.2" }, "devDependencies": { "netlify-cli": "^15.0.0" }, "engines": { "node": ">=16.x" }, "author": "j23", "license": "MIT" }
Step 2 Configure Netlify#
1. Create a Site#
As shown in the image:
- Select your GitHub repository used to store the Netlify configuration files.
2. Configure Custom Domain#
All basic operations have been completed. If you do not need to configure the backend, you only need to modify the link in the Netlify configuration file to change the website you want to proxy.
Modify the content as follows (Note! The website format must be https://example.com/, do not remove https:// or the trailing /):
[[redirects]]
from = "/*"
to = "【modify here】:splat"
status = 200
Optional Content#
I. GitHub Login#
Use GitHub for OAuth login to ensure that only authorized users can access the configuration features.
Step 1:#
Open the following URL and fill in the configuration https://github.com/settings/applications/new
Step 2:#
Obtain the Client ID and generate Client secrets for later use.
2.
Warning!
Please keep your Client secrets safe!
II. Configure the Key for Modifying the Repository#
-
Open the following URL https://github.com/settings/tokens
-
Fill in the relevant content, select non-expiring, and check the repository option.
- Obtain the key.
Warning!
Please keep your key safe!
III. Configure Netlify#
Step 1: Create a Site#
As shown in the image:
- Select your backend repository.
- Fill in the environment variables.
ALLOWED_USERS: Your GitHub usernames authorized to use the backend, separated by commas. If there is only one user, no comma is needed. For example: username1,username2 GITHUB_CLIENT_ID: Your CLIENT_ID GITHUB_CLIENT_SECRET: Your CLIENT_SECRET GITHUB_TOKEN: Your GitHub account key JWT_SECRET: A random string of unlimited length, such as wfhehfihwjdwefnabwaieu72dub3uigfvqty2UGQWADIWGADIY3UGF378
If you miss this step, you can find and supplement it on the page below.
Step 2. Configure Custom Domain#
Conclusion#
Thank you very much for reading this far. If you have any questions, feel free to ask in the comments! If possible, please subscribe to my website: https://jdjwzx233.cn/Subscribe