Surge

Webホスティング静的サイトCLIプロトタイプシンプル個人開発

Webホスティングプラットフォーム

Surge

概要

Surgeはフロントエンド開発者向けのシンプルな静的サイトホスティングサービスです。コマンドライン一つでデプロイ可能な手軽さが特徴で、プロトタイプや個人プロジェクトに最適です。シンプルさを重視する開発者に支持されるニッチなサービスで、大規模なエンタープライズ向けではないが、個人開発者や学習目的での利用が継続しています。

詳細

2014年にローンチされたSurgeは、「静的Webホスティングを極限まで簡単にする」というコンセプトで開発されたサービスです。6つのキーストローク(surgeとEnter)だけでプロジェクトをオンラインに公開できるという革新的なアプローチで注目を集めました。コマンドライン中心の設計により、Grunt、Gulp、npmなどの既存のビルドツールとの統合が容易で、フロントエンド開発ワークフローにシームレスに組み込めます。無料プランでも十分な機能を提供し、学習目的やプロトタイピングに最適化されています。

メリット・デメリット

メリット

  • 極限のシンプルさ: わずか6キーストロークでデプロイ完了
  • コマンドライン中心: 開発ワークフローへの自然な統合
  • 迅速なデプロイ: 設定不要で即座にオンライン公開
  • 無料で十分: 基本機能は無料で利用可能
  • 軽量: 余計な機能がなく動作が軽快
  • 学習に最適: 初心者にも理解しやすいシンプルな構造
  • カスタムドメイン対応: 無料プランでもカスタムドメイン利用可能

デメリット

  • 機能が限定的: 高度な機能やプラグインはなし
  • 静的サイトのみ: サーバーサイド処理は一切不可
  • スケーラビリティの低さ: 大規模サイトには不向き
  • サポート体制: 個人向けサービスのためサポートが限定的
  • エコシステム: 他サービスとの統合機能が少ない

参考ページ

書き方の例

基本的なセットアップとプロジェクト設定

# Surge CLIのインストール
npm install -g surge

# 最初のデプロイ(アカウント作成も同時に)
# プロジェクトディレクトリで実行
surge

# 特定のディレクトリを指定してデプロイ
surge ./dist

# カスタムドメインでデプロイ
surge ./dist my-awesome-project.surge.sh

# ログイン状態確認
surge whoami

# ログアウト
surge logout
// package.json - npm scriptsでのSurge統合
{
  "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"
  }
}

静的サイトデプロイ

# 基本的なデプロイ手順
cd my-website
npm run build    # ビルド実行
surge ./dist     # Surgeでデプロイ

# 初回デプロイ時の対話例
# email: [email protected]
# password: ********
# project: ./dist
# domain: random-name-1234.surge.sh

# 同じドメインに再デプロイ
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 }}

フレームワーク統合(Next.js、React、Vue)

// next.config.js - Next.js静的エクスポート
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'export',
  trailingSlash: true,
  images: {
    unoptimized: true
  },
  
  // 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設定
import { defineConfig } from 'vite';

export default defineConfig({
  base: './',  // Surge用の相対パス設定
  build: {
    outDir: 'dist',
    assetsDir: 'assets',
  },
});

カスタムドメインとSSL

# カスタムドメインの設定
# 1. ドメインでデプロイ
surge ./dist example.com

# 2. DNS設定(CNAMEレコード)
# example.com -> na-west1.surge.sh

# 3. WWWサブドメインの設定
surge ./dist www.example.com

# SSL証明書(自動で有効化)
# SurgeはHTTPSを自動で有効化

# ドメインの確認
surge list

# プロジェクトの削除
surge teardown example.com
<!-- CNAME ファイル -->
<!-- プロジェクトルートに CNAME ファイルを作成 -->
example.com
# 複数ドメインでの運用例
surge ./dist staging.example.com    # ステージング環境
surge ./dist example.com            # 本番環境
surge ./dist v2.example.com         # 新バージョンテスト

サーバーレスファンクションとAPI

<!-- Surge は静的サイトのみのため、疑似的なAPI実装 -->

<!-- index.html - JSONファイルベースの疑似API -->
<!DOCTYPE html>
<html>
<head>
  <title>Static API Example</title>
</head>
<body>
  <div id="content"></div>
  
  <script>
    // 静的JSONファイルからデータ取得
    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 - 静的データファイル
[
  {
    "id": 1,
    "name": "John Doe",
    "email": "[email protected]"
  },
  {
    "id": 2,
    "name": "Jane Smith",
    "email": "[email protected]"
  }
]
// build-api.js - ビルド時にAPIデータ生成
const fs = require('fs');
const path = require('path');

// APIディレクトリの作成
const apiDir = path.join(__dirname, 'dist', 'api');
if (!fs.existsSync(apiDir)) {
  fs.mkdirSync(apiDir, { recursive: true });
}

// 静的データの生成
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' }
];

// JSONファイルの出力
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と本番最適化

# .surgeignore - デプロイ除外ファイル
node_modules/
src/
.git/
.gitignore
README.md
package.json
package-lock.json
.env*
*.log
// scripts/deploy.js - カスタムデプロイスクリプト
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}`);
  
  // ビルド実行
  exec('npm run build', (error, stdout, stderr) => {
    if (error) {
      console.error(`Build error: ${error}`);
      return;
    }
    
    console.log('✅ Build completed');
    
    // Surgeでデプロイ
    exec(`surge ./dist ${domain}`, (error, stdout, stderr) => {
      if (error) {
        console.error(`Deploy error: ${error}`);
        return;
      }
      
      console.log(`✅ Deployed successfully to https://${domain}`);
    });
  });
};

// コマンドライン引数から環境を取得
const env = process.argv[2] || 'staging';
deploy(env);
// package.json - 完全なビルド・デプロイワークフロー
{
  "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運用のベストプラクティス

# 1. プロジェクトの整理
surge list                           # 現在のプロジェクト一覧

# 2. 古いプロジェクトの削除
surge teardown old-project.surge.sh  # 不要なプロジェクト削除

# 3. ドメインの管理
surge list | grep example.com        # 特定ドメインの検索

# 4. 認証トークンの管理
surge token                          # 現在のトークン表示

# 5. 環境別デプロイ
surge ./dist feature-branch.surge.sh    # フィーチャーブランチ
surge ./dist staging.example.com        # ステージング
surge ./dist example.com                # 本番

# 6. トラブルシューティング
surge whoami                         # ログイン状態確認
surge login                          # 再ログイン
surge --help                         # ヘルプ表示