Deploying Next.js Static Sites to Cloudflare Pages

A comprehensive guide to deploying your Next.js static export applications to Cloudflare Pages for fast, secure, and cost-effective hosting.

Labo360 Team
5 min read

Deploying Next.js Static Sites to Cloudflare Pages

Cloudflare Pages offers an excellent hosting solution for static sites with global CDN distribution, automatic HTTPS, and seamless Git integration. In this guide, we'll walk through deploying a Next.js application with static export to Cloudflare Pages.

Why Choose Cloudflare Pages?

Cloudflare Pages provides several advantages for hosting static sites:

  • Global CDN: Your site is served from Cloudflare's global network
  • Free Tier: Generous free tier with unlimited requests
  • Automatic HTTPS: SSL certificates are handled automatically
  • Git Integration: Deploy directly from GitHub, GitLab, or Bitbucket
  • Preview Deployments: Every pull request gets a preview URL
  • Edge Functions: Add server-side functionality when needed

Preparing Your Next.js App for Static Export

1. Configure Next.js for Static Export

First, update your next.config.js to enable static export:

/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
images: {
unoptimized: true
},
trailingSlash: true,
assetPrefix: process.env.NODE_ENV === 'production' ? '/your-app' : '',
}
module.exports = nextConfig

2. Handle Dynamic Routes

For dynamic routes, you need to generate static paths:

// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await getAllPosts()
return posts.map((post) => ({
slug: post.slug,
}))
}

3. Update Image Handling

Since we're using static export, optimize your images:

// Use next/image with unoptimized flag
import Image from 'next/image'
export default function MyImage() {
return (
<Image
src="/my-image.jpg"
alt="Description"
width={500}
height={300}
// unoptimized is set globally in next.config.js
/>
)
}

Setting Up Cloudflare Pages

Method 1: Git Integration (Recommended)

  1. Push your code to GitHub:

    git add .
    git commit -m "Initial commit"
    git push origin main
  2. Connect to Cloudflare Pages:

  3. Configure Build Settings:

    Framework preset: Next.js (Static HTML Export) Build command: npm run build Build output directory: out Root directory: / (leave blank if at root)
  4. Environment Variables (if needed):

    NODE_ENV=production NEXT_PUBLIC_SITE_URL=https://your-domain.pages.dev

Method 2: Direct Upload

For projects not in Git or one-time deployments:

  1. Build your project locally:

    npm run build
  2. Upload the out directory:

    • Go to Cloudflare Pages
    • Choose "Upload assets"
    • Upload the contents of the out folder

Advanced Configuration

Custom Domain Setup

  1. Add your domain:

    • In your Pages project, go to "Custom domains"
    • Click "Set up a custom domain"
    • Enter your domain name
  2. Update DNS:

    • Add a CNAME record pointing to your .pages.dev domain
    • Or transfer your domain to Cloudflare for automatic configuration

Security Headers

Create a _headers file in your public directory:

/* X-Frame-Options: DENY X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: camera=(), microphone=(), geolocation=()

Redirects and Rewrites

Create a _redirects file in your public directory:

# Redirect old blog URLs /old-blog/* /blog/:splat 301 # SPA fallback for client-side routing /* /index.html 200

Performance Optimization

1. Image Optimization

// Use WebP format for better compression
import Image from 'next/image'
export default function OptimizedImage() {
return (
<picture>
<source srcSet="/image.webp" type="image/webp" />
<Image
src="/image.jpg"
alt="Fallback"
width={500}
height={300}
/>
</picture>
)
}

2. Asset Optimization

// next.config.js
const nextConfig = {
output: 'export',
images: { unoptimized: true },
compress: true,
poweredByHeader: false,
generateEtags: false,
}

3. Bundle Analysis

Add bundle analyzer to understand your build:

npm install --save-dev @next/bundle-analyzer
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
})
module.exports = withBundleAnalyzer(nextConfig)

Troubleshooting Common Issues

Build Failures

Issue: Build fails with export errors

Error: Image Optimization using the default loader is not compatible with export.

Solution: Enable unoptimized images:

// next.config.js
module.exports = {
images: {
unoptimized: true
}
}

404 Errors

Issue: Routes return 404 in production

Solution: Ensure you have trailingSlash: true and proper static generation:

// For dynamic routes, ensure generateStaticParams is implemented
export async function generateStaticParams() {
// Return all possible param combinations
}

Environment Variables

Issue: Environment variables not working

Solution: Use NEXT_PUBLIC_ prefix for client-side variables:

# In Cloudflare Pages settings
NEXT_PUBLIC_API_URL=https://api.example.com

Monitoring and Analytics

1. Cloudflare Analytics

Enable Cloudflare Analytics for:

  • Page views and unique visitors
  • Performance metrics
  • Geographic distribution
  • Bot traffic analysis

2. Real User Monitoring (RUM)

Add performance monitoring:

// lib/analytics.js
export function initAnalytics() {
if (typeof window !== 'undefined' && process.env.NODE_ENV === 'production') {
// Initialize your analytics service
}
}

Continuous Deployment Workflow

Set up automated deployments:

# .github/workflows/deploy.yml
name: Deploy to Cloudflare Pages
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npm run build
- run: npm run test
- name: Deploy to Cloudflare Pages
uses: cloudflare/pages-action@v1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: your-project-name
directory: out

Conclusion

Cloudflare Pages provides an excellent platform for hosting Next.js static sites with minimal configuration and maximum performance. The combination of global CDN, automatic deployments, and generous free tier makes it an ideal choice for most static sites.

Key takeaways:

  • Configure Next.js for static export
  • Use Git integration for automated deployments
  • Implement proper redirects and security headers
  • Monitor performance and optimize accordingly

Ready to deploy your Next.js app? Start with the Git integration method for the smoothest experience, and don't forget to set up a custom domain for a professional touch.


Need help with your deployment? Join our community or reach out for personalized assistance.