MeWrite Docs

HTTP 401 Unauthorized

HTTPリクエストで認証が必要なリソースに未認証でアクセスした際に返されるエラー

概要

401 Unauthorized は、リクエストに有効な認証情報が含まれていない場合にサーバーが返すHTTPステータスコードです。認証トークンの欠落、期限切れ、または不正な認証情報が原因で発生します。

エラーメッセージ

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"
1
2
3
4
5
{
  "error": "Unauthorized",
  "message": "Access token is missing or invalid",
  "statusCode": 401
}
curl: (22) The requested URL returned error: 401

原因

  1. 認証ヘッダーが未設定: Authorization ヘッダーがリクエストに含まれていない
  2. トークンの期限切れ: アクセストークンやJWTの有効期限が切れている
  3. トークンが無効: 不正なトークン、改ざんされたトークン、または取り消されたトークン
  4. 認証スキームの不一致: Bearer が必要なのに Basic を送信している等
  5. セッション切れ: サーバー側のセッションがタイムアウトしている

解決策

1. Authorization ヘッダーを正しく設定する

1
2
3
4
5
6
7
8
9
# Bearer Token(最も一般的)
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  https://api.example.com/resource

# Basic Auth
curl -u username:password https://api.example.com/resource
# または
curl -H "Authorization: Basic $(echo -n 'user:pass' | base64)" \
  https://api.example.com/resource

2. JavaScript(fetch / axios)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// fetch
const response = await fetch('https://api.example.com/resource', {
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'Content-Type': 'application/json'
  }
});

if (response.status === 401) {
  // トークンをリフレッシュして再試行
  const newToken = await refreshAccessToken();
  const retryResponse = await fetch('https://api.example.com/resource', {
    headers: {
      'Authorization': `Bearer ${newToken}`,
      'Content-Type': 'application/json'
    }
  });
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// axios インターセプターで自動リフレッシュ
import axios from 'axios';

const api = axios.create({
  baseURL: 'https://api.example.com'
});

api.interceptors.request.use(config => {
  const token = localStorage.getItem('access_token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

api.interceptors.response.use(
  response => response,
  async error => {
    if (error.response?.status === 401) {
      const newToken = await refreshToken();
      localStorage.setItem('access_token', newToken);
      error.config.headers.Authorization = `Bearer ${newToken}`;
      return api.request(error.config);
    }
    return Promise.reject(error);
  }
);

3. Python(requests)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests

# Basic Auth
response = requests.get(
    'https://api.example.com/resource',
    auth=('username', 'password')
)

# Bearer Token
headers = {'Authorization': f'Bearer {access_token}'}
response = requests.get(
    'https://api.example.com/resource',
    headers=headers
)

if response.status_code == 401:
    # トークンリフレッシュ
    new_token = refresh_access_token()
    headers = {'Authorization': f'Bearer {new_token}'}
    response = requests.get(
        'https://api.example.com/resource',
        headers=headers
    )

4. トークンリフレッシュの実装例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
async function refreshAccessToken() {
  const refreshToken = localStorage.getItem('refresh_token');
  const response = await fetch('https://api.example.com/auth/refresh', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ refresh_token: refreshToken })
  });

  if (!response.ok) {
    // リフレッシュトークンも無効 → 再ログインが必要
    window.location.href = '/login';
    throw new Error('Session expired');
  }

  const data = await response.json();
  localStorage.setItem('access_token', data.access_token);
  return data.access_token;
}

5. デバッグ手順

1
2
3
4
5
6
7
8
# レスポンスヘッダーを確認(WWW-Authenticate で認証方式を確認)
curl -v https://api.example.com/resource 2>&1 | grep -i 'www-authenticate'

# トークンの中身を確認(JWT の場合)
echo "eyJhbGciOiJIUzI1NiIs..." | cut -d'.' -f2 | base64 -d 2>/dev/null | jq .

# トークンの有効期限を確認
echo "eyJhbGciOiJIUzI1NiIs..." | cut -d'.' -f2 | base64 -d 2>/dev/null | jq '.exp | todate'

関連エラー

HTTP の他のエラー

最終更新: 2026-02-03