---
title: "How to Migrate from Cloudflare Pages to Railway"
description: "Migrating from Cloudflare Pages to Railway?  Use this guide to help you with the process!"
date: 2024-12-13T00:00:00.000Z
updated: 2026-10-29T11:13:22.943Z
topics: ["Guides"]
tags: []
url: https://railway.com/guides/migrate-from-cloudflare-pages-to-railway
---

# How to Migrate from Cloudflare Pages to Railway

This guide is about migrating your app from Cloudflare Pages to Railway platform.

## **Why Migrate?**

- **Widely used database features**: Railway provides managed hosting for PostgreSQL, MySQL, and Redis, which Cloudflare Pages does not support.
- **Server-side languages and frameworks**: Railway supports backend languages like PHP, Ruby, Java, Go, and Python, whereas Cloudflare Pages only supports serverless functions in JavaScript.
- **Support for Dockerfiles**: Railway allows hosting apps using Dockerfiles or linking deployments to container registries like Docker Hub.

## **Differences between Cloudflare Pages and Railway**

![Cloudfare Pages Architecture](https://cms.railway.com/media/a515180de236af8ba93ea0376ea0aa30514314b6eec24e56beeb784f9433a563.png)

![Railway Architecture](https://cms.railway.com/media/1b574899caae30764780d082f5a5491cefc1c75d29e7c250a6542cc51a7a105f.png)

## **Prerequisites**

- Railway Account
- Cloudflare Account
- GitHub Account

## **Migration Cases**

This guide will look at the following cases of migration:

- [Cloudflare Project is based on Direct Upload](#case-1-cloudflare-project-is-based-on-direct-upload)
- [Cloudflare Pages Project uses Framework Preset (SSG)](#case-2-cloudflare-pages-project-uses-framework-preset-(ssg))
- [Cloudflare Pages Project uses Framework Preset (SSR)](#case-3-cloudflare-pages-project-uses-framework-preset-(ssr))
- [Cloudflare Pages Project uses Cloudflare Pages Functions](#case-4-cloudflare-pages-project-uses-functions)
- [Cloudflare Pages Project has Redirects](#case-5-cloudflare-pages-project-has-redirects)
- [Cloudflare Pages Project has Headers](#case-6-cloudflare-pages-project-has-headers)

## General Migration Procedure

*The general migration procedure is a required step for each migration case.  This section will be referenced throughout the article.  For now, you can simply review the section and move on to your specific migration case.*

The general migration procedure is as follows:

**Create Railway Project**

![Creating a new Railway Project](https://cms.railway.com/media/dc42649466ab9fbfff48d4b637be66378df3ce0412c235617d410436f8638884.png)

Log in to your Railway dashboard and create a new project. Link your Cloudflare project's GitHub repo to your Railway project to create a new service.

**Configure Environment**

Add environment variables. 

![Adding environment variables to Railway service](https://cms.railway.com/media/74a4d84c4b1c01b9169b5c5e71becd74785af6c6002b1d8313904da4c0b2c67e.png)

Optionally, set up build commands, start commands, and configure providers.

![Configuring build and deploy options in Railway](https://cms.railway.com/media/79dfc7b7bfacb72f18b6ef165cb7894a783c5ba32b4a836c66d1cfe9776cca9b.png)

![Configuring start command in Railway](https://cms.railway.com/media/8304232508d2cc4e05b6bc6c3fd00857aea075f64cfd55ef5d1d64f771aa3ac6.png)

**Deploy and Test**

Deploy the service and [generate a domain](https://docs.railway.com/networking/public-networking), then test the deployment.

**Delete Cloudflare Project**
After successfully deploying your project on Railway, you can safely delete your Cloudflare project.

## **Case 1: Cloudflare Project is based on Direct Upload**

If your Cloudflare Pages site is deployed using the “Direct Upload” method, follow these steps.

### **Step 1: Create GitHub repo**

Log in to your GitHub account.

```bash
gh auth login

```

Create a new GitHub repository and clone it to your local machine.

```bash
gh repo create <REPO-NAME> --public --clone

```

### Step 2: Update GitHub repo

Move your Cloudflare Pages site folder into the repo.

```bash
mv <CLOUDFLARE PAGES FOLDER>/ <REPO-NAME>/public/

```

Make sure the folder is named `public` or `index` or `dist`. 

### Step 3: Upload to GitHub

Stage and commit the changes and push the repo to GitHub.

```bash
git commit -a -m "Added site"
git push
```

### Step 4: Deploy to Railway

Follow the [General Migration](#general-migration-procedure) procedure.

## **Case 2: Cloudflare Pages Project uses Framework Preset (SSG)**

This case is for Cloudflare Pages project that use a framework preset, for example Next.js, Nuxt.js and so on. Some frameworks like Next.js support more than one mode of deployment, Static Site Generation(SSG) or Server Side Rendering (SSR). This case looks at apps deployed using SSG. This means a static file output is generated after the build process.

### **Step 1: Install app on Railway**

Follow the [General Migration](#general-migration-procedure) procedure up until the [Configure Environment](https://app.notion.com/p/How-to-Migrate-from-Cloudflare-Pages-to-Railway-10d0e4c54563808da4cdeeb28a235817?pvs=21) step.

### **Step 2: Add Build commands**

In your Railway service’s **Settings** select **Custom Build Command** to enter the build command for your project. For example a Nuxt.js app uses the command `npm run build`.

Since Railway uses Nixpacks for builds you may not need to enter a build command as it has support for a very large set of languages’ build procedures. For a Nuxt.js app, Nixpacks will use the `package.json` file to determine the build command and build an image. 

Here's a [table](https://github.com/Marktawa/railway-table/blob/main/ssr.md) of the framework presets supported by Cloudflare Pages, the build output directories, build commands, and providers to enter in your Railway dashboard for each framework.

### **Step 3: Setup Providers**

In the **Providers** section of your service's **Settings** add the Providers that correspond to your app's framework. For example, a Next.js app will use the **Node.js** provider to build the app and the **Staticfile** to deploy the app as a static site. Refer to the table above for your framework's provider settings.

### **Step 4: Setup Start Command**

Under the **Deploy** section of your service's **Settings** click **Custom Start Command** and add the following command:

```bash
sed -i "s|/app|/app/<BUILD_DIRECTORY>|g" /assets/nginx.conf && nginx -s /assets/nginx.conf

```

Replace `<BUILD_DIRECTORY>` with the build directory for your app's framework. For instance, a Next.js app will use:

```bash
sed -i "s|/app|/app/out|g" /assets/nginx.conf && nginx -s /assets/nginx.conf

```

Before your app is deployed Nginx is configured to use the build directory for your app as the root directory to serve the site.

### **Step 5: Deploy app**

Follow the rest of the [General Migration](#general-migration-procedure) steps to complete migration.

## **Case 3: Cloudflare Pages Project uses Framework Preset (SSR)**

### **Step 1: Remove Cloudflare dependencies**

If you are using the following frameworks you need to remove Cloudflare Pages dependencies first before proceeding.

- Analog
- Astro
- Next.js
- Nuxt
- Remix
- Gatsby
- Solid
- Qwik
- SvelteKit

### **Next.js**

For example, a Next.js project configured for SSR using Cloudflare would first go through the following steps to remove Cloudflare dependencies.

In your project's root directory, uninstall `@cloudflare/next-on-pages`:

```bash
npm uninstall --save-dev @cloudflare/next-on-pages

```

Remove the `wrangler.toml` file:

```bash
rm wrangler.toml

```

Remove the highlighted code inside `next.config.mjs`:

```jsx
//Remove this one line
import { setupDevPlatform } from '@cloudflare/next-on-pages/next-dev';

/** @type {import('next').NextConfig} */const nextConfig = {};

//Remove the following 3 lines
if (process.env.NODE_ENV === 'development') {
   await setupDevPlatform();
 }

export default nextConfig;

```

Remove the Edge runtime for each server-rendered route for your Next.js project.

```bash
find . -type f -exec sed -i '/export const runtime = "edge";/d' {} +

```

Remove the following scripts from your `package.json`:

```json
"pages:build": "npx @cloudflare/next-on-pages",
"preview": "npm run pages:build && wrangler pages dev",
"deploy": "npm run pages:build && wrangler pages deploy"

```

### **Step 2: Configure Build and Start Commands**

Update the npm `build` script  and `start` scripts in your app's `package.json` file. 

Refer to this [table](https://github.com/Marktawa/railway-table/blob/main/ssr.md) to get the start and build commands for your framework.

> NOTE: *This guide only looks at JavaScript frameworks which support SSR as they are the only ones supported by Cloudflare Pages*
> 

Commit and push your changes to your GitHub repo.

### **Step 3: Deploy app**

Follow the [General Migration](#general-migration-procedure) steps to complete migration. 

## **Case 4: Cloudflare Pages Project uses Functions**

Does your app use functions? If so, you need to create a new service with a Node.js runtime to serve the function.

### **Step 1: Convert Cloudflare Function into Node.js runtime**

Before creating a new Railway project, a few changes have to be made to the code to make it work in a Node.js runtime environment. 

**Example Using Express**

For example, if you had a Cloudflare Pages project with a function like `helloworld.js` in your `functions` folder with the following folder structure:

```bash
.
├── functions
│   └── helloworld.js
└── public
    ├── screen.css
    ├── script.js
    ├── index.html
    └── image.jpg

```

And `helloworld.js` looking as follows:

```jsx
export async function onRequest() {
    return new Response(
        '<h1>Hello World!</h1>',
        {
            headers: { "Content-Type": "text/html" }
        }
    );
  }

```

Move the `helloworld.js` file to the root of your project and rename it `index.js` and replace the code with the following:

```jsx
const express = require('express');
const app = express();

app.get('/helloworld', (req, res) => {
    res.setHeader('Content-Type', 'text/html');
    res.send('<h1>Hello World!</h1>');
});

app.listen(8080);

```

Delete the `functions` folder and add `express` as your server:

```bash
npm install express

```

> NOTE: You can use any server framework you are familiar with like Fastify, Koa.js, etc. This example just happens to use Express
> 

Next, configure Express to serve the static files for your project. Update `index.js` with `express.static` function which handles static files:

```jsx
const express = require('express');
const app = express();

app.get('/helloworld', (req, res) => {
    res.setHeader('Content-Type', 'text/html');
    res.send('<h1>Hello World!</h1>');
});

app.use(express.static('public'));

app.listen(8080);

```

Assuming your project uses a folder named `public` to host static files. The updated folder structure for your project looks as follows:

```bash
├── index.js
├── package.json
└── public
    ├── screen.css
    ├── script.js
    ├── index.html
    └── image.jpg

```

### **Step 2: Configure Start command**

A `package.json` file with the **start** commands should accompany the script. On deploying, Railway will use Nixpacks to build the image for the Node.js runtime and deploy your app.

For the example above, the `package.json` will include:

```json
{
    "scripts": {
        "start": "node index.js"
    },
    "dependencies": {
        "express": "^4.21.0"
    }
}

```

Commit and push your changes to your GitHub repo.

### **Step 3: Deploy app**

Follow the [General Migration](#general-migration-procedure) steps to complete migration. 

## **Case 5: Cloudflare Pages Project has Redirects**

Cloudflare Pages supports redirects. If you are migrating a project with redirects, you have to reconfigure it.

### **Step 1: Configure a web server for redirects**

Most web servers support configuring redirects for your website. Pick a web server and configure your project's redirects to work with it. In the following example, we will use an Nginx web server.

Here's a simple example of the folder structure for a Cloudflare Pages project that supports redirects:

```bash
.
└── public
    ├── screen.css
    ├── script.js
    ├── index.html
    ├── image.jpg
    └── _redirects

```

The `_redirects` file is used to configure redirects.

Let's assume your `_redirects` has the following:

```
/home / 301
/public / 302

```

`/home / 301` means that any request to `/home` will be redirected to the root URL `/` with a 301 status code.

`/public / 302` means that any request to `/public` will be redirected to the root URL `/` with a 302 status code.

These redirects will be set using an Nginx web server.

Create a `nginx.conf` file in the root directory of your project. Use it to configure the redirects for your website.

Add the following code:

```
server {
    listen    0.0.0.0:80;
    gzip  	  on;

    # Define redirects here
    location /home {
        return 301 /;
    }

    location /public {
        return 302 /;
    }

    # Serve your site
    location / {
        root /usr/share/nginx/html;
    }
}

```

Delete the `_redirects` file as you no longer need it.

### **Step 2: Create Dockerfile**

Railway as mentioned earlier supports deployment via Dockerfiles.

Create a Dockerfile to help Railway build and deploy your project. Add the following to the Dockerfile:

```docker
# Use the official Nginx image from the Docker Hub
FROM nginx:alpine

# Copy your static site files to the Nginx html directory
COPY ./public /usr/share/nginx/html

# Copy your custom Nginx configuration file
COPY ./nginx.conf /etc/nginx/conf.d/default.conf

# Expose port 80 to the outside world
EXPOSE 80

# Start Nginx when the container starts
CMD ["nginx", "-g", "daemon off;"]

```

This Dockerfile uses the custom Nginx configuration you created earlier.

This should be the new folder structure for your project:

```
.
├── Dockerfile
└── public
    ├── screen.css
    ├── script.js
    ├── index.html
    └── image.jpg

```

Test your configuration locally before pushing changes to your GitHub repo.

Build and run the Docker image:

```bash
docker build -t my-site .
docker run -d -p 80:80 my-site

```

### Step 3: Deploy app

Follow the [General Migration](#general-migration-procedure) steps to complete migration.

## **Case 6: Cloudflare Pages Project has Headers**

Cloudflare Pages also supports headers. A `_headers` file in the root of the output folder is used to attach headers to Cloudflare Pages responses.

Here's an example layout for a Cloudflare Pages Project that supports attaching headers to responses:

```bash
.
└── public
    ├── screen.css
    ├── script.js
    ├── index.html
    ├── image.jpg
    └── _headers

```

### **Step 1: Set Headers using Web Server**

Let's assume your `_headers` file has the following settings:

```
/*
  Content-Security-Policy: default-src 'self';

```

This is a Content-Security-Policy. It prevents a wide range of attacks, including XSS.

To set headers for your static site on Railway, your site must be served using a web server which supports headers. For this example we will use Nginx.

In the root of your project directory, create an `nginx.conf` file add the following code:

```
http {
    include       mime.types;
    default_type  application/octet-stream;

    server {
        listen       80;

        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;

            # Content Security Policy header
            add_header Content-Security-Policy "default-src 'self'";
        }
    }
}

```

Delete the `_headers` file as you no longer need it.

### **Step 2: Create Dockerfile**

Create a `Dockerfile` like the one in [Create Dockerfile](https://app.notion.com/p/How-to-Migrate-from-Cloudflare-Pages-to-Railway-10d0e4c54563808da4cdeeb28a235817?pvs=21) step in the case for [Cloudflare Projects wth Redirects](https://app.notion.com/p/How-to-Migrate-from-Cloudflare-Pages-to-Railway-10d0e4c54563808da4cdeeb28a235817?pvs=21).

### **Step 3: Deploy to Railway**

Follow the [General Migration](#general-migration-procedure) steps to complete migration.

## **Conclusion**

While this migration guide covers the most common scenarios for moving from Cloudflare Pages to Railway, it's not exhaustive. Every project has its unique characteristics and requirements. However, the approaches outlined here can serve as a foundation for handling more specialized cases.

### **Next Steps:**

1. [**Tweak your Deployment**](https://docs.railway.com/deployments)
2. [**Optimize Performance**](https://docs.railway.com/deployments/optimize-performance)
3. [Add a Database Service](https://docs.railway.com/databases)