jdjwzx233

jdjwzx233

如期待奇迹发生,那唯有不停伸手去抓紧,去把握才行
bilibili
github
email

Set up a reverse proxy using Netlify

1. Disclaimer

At the time of writing this article, my aim is 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 herein. It is advisable to conduct thorough testing and risk assessments 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:

  1. Legal Compliance: Ensure that the reverse proxy set up is not used for activities that violate laws and regulations, including but not limited to circumventing censorship, illegal data transmission, or other unlawful purposes.
  2. 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 adhere to data protection requirements.
  3. 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.
  4. Information Transparency: Ensure that the service information provided is clear and transparent, allowing users to be fully aware of the purpose of the reverse proxy and the potential risks involved.

Introduction/Abstract#

In today's internet environment, many users face difficulties accessing 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 limits their flexibility and usability. To address these issues, 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 reached via IP addresses using custom domain names.

In this article, we will utilize 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 robust features, making it easy for even users who are not very tech-savvy 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
Items marked with (optional) are related to backend content and can be skipped if not needed.

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:

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/17/1726582387299_23298110f4e294999cce8e5ddb9919ed.png

2. Create a Configuration File#

Create a new configuration file named netlify.toml.

As shown in the image:

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/17/image_b32990708afc79c8ac57533641e18942.png

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:

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/17/1726582387299_23298110f4e294999cce8e5ddb9919ed.png

Files included in the repository (optional):#
│  index.html
│  package.json
│  README.md

└─netlify
  └─functions
    auth-callback.js
    check-auth.js
    update-redirect.js
  1. index.html: The main frontend page of the application, containing the user login and reverse proxy configuration form. (optional)

Friendly Reminder
Please fill in your GitHub client ID in the code where it says 【here fill in your GitHub clientid】. See below for how to obtain it.

<!DOCTYPE html>
<html lang="en">
<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 the proxied 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 URL is in the form of 'https://example.com'. A possible correct input could be: ${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, expected in 1 minute.';
                successContainer.style.display = 'block';
                errorContainer.style.display = 'none';
                redirectButton.style.display = 'inline-block';
                redirectButton.addEventListener('click', () => {
                    window.location.href = 'https://g.j23.one/';
                });
            } else {
                errorMessageElement.innerText = `Configuration failed: ${result.message}`;
                errorContainer.style.display = 'block';
                successContainer.style.display = 'none';
            }
        });

        // Initialize the authentication check
        checkAuthentication();
    </script>
</body>
</html>
  1. /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}`
        };
    }
};
  1. /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 })
        };
    }
};
  1. /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' })
        };
    }
};
  1. 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:

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/17/image_24ce560b9f2dc57d0d6f51bfebae6b71.png

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/17/image_d0c9459ade47c217db7d74d4c0dc815f.png

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/17/image_ee9fc4b5245b51661e8a91a2c509c959.png

  1. Select your GitHub repository for storing the Netlify configuration files.

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/17/image_1f40a4eb848a567c69eee524172ba04a.png

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/17 / 屏幕截图_17-9-2024_23297_app.netlify.com_9d63c0ed9504f5a945c156f026d8fc1f.jpeg

2. Configure Custom Domain#

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/17/image_03b28a83de8ee0c91accd703fdca989d.png

At this point, 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 URL 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

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/17/image_08ba52609ba4bfe62f2563933cbba566.png

Step 2:#

Obtain the Client ID and generate Client secrets for later use.

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/17/image_038e6d1208bd8e0990b3b595a40f256e.png
2.
https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/17/image_0572103207489f7a3ae48fd3a3e2a2b6.png

Warning!
Please keep your Client secrets safe!

II. Configure the Key for Modifying the Repository#

  1. Open the following URL https://github.com/settings/tokens

    https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/18/image_29ac6da4b073ffc18764a3748bcc885a.png

  2. Fill in the relevant content, select "No expiration," and check "Repository."

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/18/image_bdfead6a17b8cdf05219e9ae7e9bd799.png

  1. Obtain the key.

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/18/image_56563b4d40ebd00c25c2ddbc0aea9565.png

Warning!
Please keep your key safe!

III. Configure Netlify#

Step 1: Create a Site#

As shown in the image:

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/17/image_24ce560b9f2dc57d0d6f51bfebae6b71.png

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/17/image_d0c9459ade47c217db7d74d4c0dc815f.png

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/17/image_ee9fc4b5245b51661e8a91a2c509c959.png

  1. Select your backend repository.

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/17/image_1f40a4eb848a567c69eee524172ba04a.png

  1. 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
    

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/18/image_5bb90ceb6dd257c5c3e51cd20423f929.png
If you miss this step, you can find and supplement it on the page below.

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/18/image_1184a04b95f68e809a5360a9e5925ecc.png

Step 2. Configure Custom Domain#

https://upcdn.jdjwzx233.cn/upload/Qexo/24/9/17/image_03b28a83de8ee0c91accd703fdca989d.png

Conclusion#

Thank you very much for reading this far. If you have any questions, please feel free to raise them in the comments! If possible, please subscribe to my website: https://jdjwzx233.cn/Subscribe

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.