S3 + CloudFrontでtrailing slashをつけないようにする
S3でホスティングをしている場合、リクエストされたオブジェクトが存在しなかった場合に、trailing slashをつけてリダイレクトする、という挙動をする。しかし、これが期待する動作ではなく、むしろ逆の動作をしてほしい場合には、index.html
を用意してリダイレクト属性を付けるしかないようだ。
説明書
- http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/IndexDocumentSupport.html
- https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/UsingMetadata.html
- https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/how-to-page-redirect.html
リクエストに対するS3の挙動
- リクエスト通りのobjectが存在すれば、S3はそのファイルを返す
- オブジェクトが存在しない場合、/を追加してディレクトリを見に行く
index.html
が存在すれば、そのファイルを返すindex.html
がなければエラーを返す
方針
- /path/to/hogeをcanonical URLとする
- /path/to/hoge/を/path/to/hogeにリダイレクトする
やること
/path/to/hoge.html
を/path/to/hoge
にアップロードする/path/to/hoge/index.html
に空ファイルをアップロードする(/path/to/hoge
へのリダイレクト属性をつける)
1. /path/to/hoge.html
を/path/to/hoge
にアップロードする
require 'aws-sdk'
class S3Uploader
def initialize()
@bucket = Aws::S3::Bucket.new 'host.bucketname.s3'
@dest_dir = "#{Rails.root}/public"
end
def upload_html
paths.each do |path|
upload_article_html(path)
end
end
protected
def paths
['/year/month/day/title']
end
def upload_article_html(path)
file_name = "#{path}.html"
file_path = "#{@dest_dir}#{file_name}"
object_key = path.dup
object_key.slice!(0)
@bucket.object(object_key)
.upload_file(file_path,
cache_control: 'public, max-age=6000',
content_type: 'text/html')
end
end
2. /path/to/hoge/index.html
に空ファイルをアップロードする(/path/to/hoge
へのリダイレクト属性をつける)
空ファイルのindex.htmlは事前にFileUtils.touch(dest)
とかで用意しておく。あとはこんな感じのコードでindex.htmlをアップロードできた。
require 'aws-sdk'
class S3RedirectionIndexUploader
def initialize()
@bucket = Aws::S3::Bucket.new 'host.bucketname.s3'
@dest_dir = "#{Rails.root}/public"
end
def upload_html
paths.each do |path|
upload_redirection_index_html("#{path}/index.html", path)
end
end
protected
def paths
['/year/month/day/title']
end
def upload_redirection_index_html(path, redirect_path)
file_path = "#{@dest_dir}#{path}"
object_key = path.dup
object_key.slice!(0)
@bucket.object(object_key).upload_file(file_path,
cache_control: 'public, max-age=6000',
content_type: 'text/html',
website_redirect_location: redirect_path)
end
end