diff --git a/go.mod b/go.mod
index 0efbbd17a96ea8fb3071e5b27820d68e4f2d80d3..449d4b92bd2eae6b9808a8af40e73491c4e02f3d 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@ require (
 	github.com/antonmedv/expr v1.9.0
 	github.com/go-kit/kit v0.12.0
 	github.com/golang/protobuf v1.5.2
+	github.com/gorilla/mux v1.8.0
 	github.com/gosimple/slug v1.13.1
 	github.com/hashicorp/go-multierror v1.1.1
 	github.com/hashicorp/golang-lru v0.5.4
diff --git a/go.sum b/go.sum
index e83ee408a6b4fea9e636d7028d7d8f0bf69f82f2..d98becaf480755c380c5356f291bf3363b17a4fd 100644
--- a/go.sum
+++ b/go.sum
@@ -37,6 +37,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
+github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
 github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q=
 github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
 github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
diff --git a/pkg/files/transport/http/server.go b/pkg/files/transport/http/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..664474f712e5821470664515ea4a05556abbc4c2
--- /dev/null
+++ b/pkg/files/transport/http/server.go
@@ -0,0 +1,41 @@
+package transporthttp
+
+import (
+	"context"
+	"net/http"
+
+	"git.perx.ru/perxis/perxis-go/pkg/files"
+	"git.perx.ru/perxis/perxis-go/pkg/urlsigner"
+	"github.com/gorilla/mux"
+)
+
+func NewHTTPHandler(fs files.Files, s urlsigner.URLSigner) http.Handler {
+	m := mux.NewRouter()
+	m.Methods("GET").
+		Path("/{id}").
+		HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			if !s.Check(r.URL) {
+				w.WriteHeader(http.StatusForbidden)
+				return
+			}
+			w.Header().Set("Content-Type", "text/html; charset=utf-8")
+
+			f := &files.File{
+				ID: mux.Vars(r)["id"],
+			}
+			res, err := fs.GetFile(context.Background(), f)
+			if err != nil {
+				w.WriteHeader(http.StatusInternalServerError)
+				w.Write([]byte(err.Error()))
+				return
+			}
+
+			if res == nil || res.URL == "" {
+				w.WriteHeader(http.StatusNotFound)
+				return
+			}
+			w.Header().Set("Location", res.URL)
+			w.WriteHeader(http.StatusMovedPermanently)
+		})
+	return m
+}