Surge

Web HostingStatic SitesCLIPrototypeSimpleIndividual Development

Web Hosting Platform

Surge

Overview

Surge is a simple static site hosting service for frontend developers. Characterized by easy deployment with a single command line, it's ideal for prototypes and personal projects. A niche service supported by developers who value simplicity, not for large enterprises, but continues to be used by individual developers and for learning purposes.

Details

Launched in 2014, Surge was developed with the concept of "making static web hosting extremely simple." It gained attention with its innovative approach of being able to publish projects online with just six keystrokes (surge and Enter). The command-line-centric design makes it easy to integrate with existing build tools like Grunt, Gulp, and npm, seamlessly fitting into frontend development workflows. The free plan provides sufficient functionality, optimized for learning purposes and prototyping.

Advantages and Disadvantages

Advantages

  • Ultimate Simplicity: Deploy completed with just 6 keystrokes
  • Command-Line Centric: Natural integration into development workflows
  • Rapid Deployment: Immediate online publication without configuration
  • Free is Sufficient: Basic features available for free
  • Lightweight: Quick operation without unnecessary features
  • Perfect for Learning: Simple structure easy for beginners to understand
  • Custom Domain Support: Custom domains available even on free plan

Disadvantages

  • Limited Features: No advanced features or plugins
  • Static Sites Only: No server-side processing whatsoever
  • Low Scalability: Not suitable for large-scale sites
  • Support System: Limited support as individual-oriented service
  • Ecosystem: Few integration features with other services

Reference Pages

Code Examples

Basic Setup and Project Configuration

# Install Surge CLI
npm install -g surge

# First deployment (account creation simultaneous)
# Execute in project directory
surge

# Deploy specifying specific directory
surge ./dist

# Deploy with custom domain
surge ./dist my-awesome-project.surge.sh

# Check login status
surge whoami

# Logout
surge logout
// package.json - Surge integration with npm scripts
{
  "name": "my-project",
  "version": "1.0.0",
  "scripts": {
    "build": "webpack --mode production",
    "deploy": "npm run build && surge ./dist",
    "deploy:staging": "npm run build && surge ./dist my-project-staging.surge.sh",
    "deploy:prod": "npm run build && surge ./dist my-project.com"
  },
  "devDependencies": {
    "surge": "^0.23.1",
    "webpack": "^5.0.0"
  }
}

Static Site Deployment

# Basic deployment procedure
cd my-website
npm run build    # Execute build
surge ./dist     # Deploy with Surge

# Interactive example for first deployment
# email: [email protected]
# password: ********
# project: ./dist
# domain: random-name-1234.surge.sh

# Re-deploy to same domain
surge ./dist random-name-1234.surge.sh
# .github/workflows/surge.yml - GitHub Actions
name: Deploy to Surge

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build
        run: npm run build
        
      - name: Install Surge
        run: npm install -g surge
        
      - name: Deploy to Surge
        run: |
          if [ "$GITHUB_REF" = "refs/heads/main" ]; then
            surge ./dist ${{ secrets.SURGE_DOMAIN }}
          else
            surge ./dist pr-${{ github.event.pull_request.number }}.surge.sh
          fi
        env:
          SURGE_LOGIN: ${{ secrets.SURGE_EMAIL }}
          SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }}

Framework Integration (Next.js, React, Vue)

// next.config.js - Next.js static export
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'export',
  trailingSlash: true,
  images: {
    unoptimized: true
  },
  
  // Relative paths recommended for Surge
  assetPrefix: '',
  basePath: '',
};

module.exports = nextConfig;
# React (Create React App)
npx create-react-app my-surge-app
cd my-surge-app
npm run build
surge ./build

# Vue.js
npm create vue@latest my-vue-app
cd my-vue-app
npm install
npm run build
surge ./dist

# Vite
npm create vite@latest my-vite-app
cd my-vite-app
npm install
npm run build
surge ./dist
// vite.config.js - Vite configuration
import { defineConfig } from 'vite';

export default defineConfig({
  base: './',  // Relative path setting for Surge
  build: {
    outDir: 'dist',
    assetsDir: 'assets',
  },
});

Custom Domains and SSL

# Custom domain configuration
# 1. Deploy with domain
surge ./dist example.com

# 2. DNS configuration (CNAME record)
# example.com -> na-west1.surge.sh

# 3. WWW subdomain configuration
surge ./dist www.example.com

# SSL certificate (automatically enabled)
# Surge automatically enables HTTPS

# Domain verification
surge list

# Project deletion
surge teardown example.com
<!-- CNAME file -->
<!-- Create CNAME file in project root -->
example.com
# Multi-domain operation examples
surge ./dist staging.example.com    # Staging environment
surge ./dist example.com            # Production environment
surge ./dist v2.example.com         # New version testing

Serverless Functions and APIs

<!-- Surge is static sites only, so pseudo API implementation -->

<!-- index.html - JSON file-based pseudo API -->
<!DOCTYPE html>
<html>
<head>
  <title>Static API Example</title>
</head>
<body>
  <div id="content"></div>
  
  <script>
    // Get data from static JSON files
    async function loadData() {
      try {
        const response = await fetch('./api/users.json');
        const users = await response.json();
        
        document.getElementById('content').innerHTML = 
          users.map(user => `<p>${user.name}: ${user.email}</p>`).join('');
      } catch (error) {
        console.error('Error loading data:', error);
      }
    }
    
    loadData();
  </script>
</body>
</html>
// api/users.json - Static data file
[
  {
    "id": 1,
    "name": "John Doe",
    "email": "[email protected]"
  },
  {
    "id": 2,
    "name": "Jane Smith",
    "email": "[email protected]"
  }
]
// build-api.js - Generate API data at build time
const fs = require('fs');
const path = require('path');

// Create API directory
const apiDir = path.join(__dirname, 'dist', 'api');
if (!fs.existsSync(apiDir)) {
  fs.mkdirSync(apiDir, { recursive: true });
}

// Generate static data
const users = [
  { id: 1, name: 'John Doe', email: '[email protected]' },
  { id: 2, name: 'Jane Smith', email: '[email protected]' }
];

const posts = [
  { id: 1, title: 'Hello World', content: 'This is my first post' },
  { id: 2, title: 'Second Post', content: 'Another interesting post' }
];

// Output JSON files
fs.writeFileSync(
  path.join(apiDir, 'users.json'), 
  JSON.stringify(users, null, 2)
);

fs.writeFileSync(
  path.join(apiDir, 'posts.json'), 
  JSON.stringify(posts, null, 2)
);

console.log('Static API files generated successfully!');

CI/CD and Production Optimization

# .surgeignore - Deployment exclusion file
node_modules/
src/
.git/
.gitignore
README.md
package.json
package-lock.json
.env*
*.log
// scripts/deploy.js - Custom deployment script
const { exec } = require('child_process');
const fs = require('fs');

const ENVIRONMENTS = {
  staging: 'my-app-staging.surge.sh',
  production: 'my-app.com'
};

const deploy = (env = 'staging') => {
  const domain = ENVIRONMENTS[env];
  if (!domain) {
    console.error('Invalid environment specified');
    process.exit(1);
  }
  
  console.log(`🚀 Deploying to ${env}: ${domain}`);
  
  // Execute build
  exec('npm run build', (error, stdout, stderr) => {
    if (error) {
      console.error(`Build error: ${error}`);
      return;
    }
    
    console.log('✅ Build completed');
    
    // Deploy with Surge
    exec(`surge ./dist ${domain}`, (error, stdout, stderr) => {
      if (error) {
        console.error(`Deploy error: ${error}`);
        return;
      }
      
      console.log(`✅ Deployed successfully to https://${domain}`);
    });
  });
};

// Get environment from command line arguments
const env = process.argv[2] || 'staging';
deploy(env);
// package.json - Complete build and deploy workflow
{
  "name": "surge-project",
  "version": "1.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build && node scripts/post-build.js",
    "preview": "vite preview",
    
    "deploy:staging": "node scripts/deploy.js staging",
    "deploy:prod": "node scripts/deploy.js production",
    
    "clean": "rm -rf dist",
    "prebuild": "npm run clean",
    "postbuild": "echo 'Build completed successfully!'",
    
    "surge:list": "surge list",
    "surge:teardown": "surge teardown"
  },
  "devDependencies": {
    "vite": "^5.0.0",
    "surge": "^0.23.1"
  }
}
# Surge operation best practices

# 1. Project organization
surge list                           # Current project list

# 2. Delete old projects
surge teardown old-project.surge.sh  # Delete unnecessary projects

# 3. Domain management
surge list | grep example.com        # Search for specific domain

# 4. Authentication token management
surge token                          # Display current token

# 5. Environment-based deployment
surge ./dist feature-branch.surge.sh    # Feature branch
surge ./dist staging.example.com        # Staging
surge ./dist example.com                # Production

# 6. Troubleshooting
surge whoami                         # Check login status
surge login                          # Re-login
surge --help                         # Display help