diff --git a/photo21/middleware.py b/photo21/middleware.py
deleted file mode 100644
index 29aa5d6b1aa362b1210b128a1669e41707301451..0000000000000000000000000000000000000000
--- a/photo21/middleware.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from django.http import HttpResponseRedirect
-from django.conf import settings
-
-import re
-
-
-class LoginRequiredMiddleware:
-    """
-    If user is not accessing the site from an authorized IP, force
-    authentification.
-    """
-    def __init__(self, get_response):
-        """Init middleware"""
-        self.get_response = get_response
-        self.whitelist_re = re.compile("^/accounts/.*$")
-
-    def __call__(self, request):
-        """
-        If user is not authenticated and external, redirect to login view
-        before calling the view.
-        """
-        if not request.user.is_authenticated:
-            if not self.whitelist_re.match(request.path_info):
-                return HttpResponseRedirect(settings.LOGIN_URL)
-
-        response = self.get_response(request)
-        return response
diff --git a/photo21/settings.py b/photo21/settings.py
index 5128667686220e1918622f0fdc3642fa40640848..5188f5c405fb61151d588b02b691f1f1ef2c3245 100644
--- a/photo21/settings.py
+++ b/photo21/settings.py
@@ -77,7 +77,6 @@ MIDDLEWARE = [
     'django.middleware.clickjacking.XFrameOptionsMiddleware',
     'django.middleware.locale.LocaleMiddleware',
     'django.contrib.sites.middleware.CurrentSiteMiddleware',
-    'photo21.middleware.LoginRequiredMiddleware',
 ]
 
 ROOT_URLCONF = 'photo21.urls'
diff --git a/photo21/templates/base.html b/photo21/templates/base.html
index 44af68510b9b565a474238c6798f8d08af332e08..99248c765c94d72bcbf5041f12bbe65e2483020e 100644
--- a/photo21/templates/base.html
+++ b/photo21/templates/base.html
@@ -37,7 +37,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
                     </li>
                     {% if perms.photologue.add_gallery %}
                     <li class="nav-item">
-                        {% url 'gallery-upload' as url %}
+                        {% url 'photologue:pl-gallery-upload' as url %}
                         <a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}">{% trans 'Upload' %}</a>
                     </li>
                     {% endif %}
diff --git a/photo21/urls.py b/photo21/urls.py
index 3b6c278578717d82e40430a06382c91cf82961ab..530068a2596497104708a8f9b873324fe467bed8 100644
--- a/photo21/urls.py
+++ b/photo21/urls.py
@@ -20,17 +20,16 @@ from django.conf.urls.static import static
 
 from .views import IndexView, MediaAccess
 
-# photologue_custom overrides some photologue patterns
 urlpatterns = [
     path('', IndexView.as_view(), name='index'),
-    path('photologue/', include('photologue_custom.urls')),
-    path('photologue/', include('photologue.urls', namespace='photologue')),
+    path('', include('photologue_custom.urls', namespace='photologue')),
     path('accounts/', include('allauth.urls')),
     path('i18n/', include('django.conf.urls.i18n')),
     path('admin/', admin.site.urls),
     path('admin/doc/', include('django.contrib.admindocs.urls')),
 ]
 
+# In production media are served through NGINX with X-Accel-Redirect
 if settings.DEBUG:
     urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
 else:
diff --git a/photologue_custom/templates/photologue/gallery_detail.html b/photologue_custom/templates/photologue/gallery_detail.html
index 15c0b3211e2eafe72ba398c96944ae22edbd7aa0..e26bc9cdf396b902a0fbe345dee6b89c83f413e8 100644
--- a/photologue_custom/templates/photologue/gallery_detail.html
+++ b/photologue_custom/templates/photologue/gallery_detail.html
@@ -39,7 +39,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
 {% if gallery.extended.tags.all %}
 <p class="text-muted">
     Tags : {% for tag in gallery.extended.tags.all %}
-    <a class="badge rounded-pill bg-dark text-decoration-none" href="{% url 'tag-detail' tag.slug %}">{{ tag }}</a>
+    <a class="badge rounded-pill bg-dark text-decoration-none" href="{% url 'photologue:tag-detail' tag.slug %}">{{ tag }}</a>
     {% endfor %}
 </p>
 {% endif %}
@@ -49,14 +49,14 @@ SPDX-License-Identifier: GPL-3.0-or-later
     <div class="card-header pb-0 border-bottom-0">
         <ul class="nav nav-tabs">
             <li class="nav-item">
-                {% url 'pl-gallery' gallery.slug as url %}
+                {% url 'photologue:pl-gallery' gallery.slug as url %}
                 <a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}">
                     {% trans "All pictures" %}
                 </a>
             </li>
             {% for owner in owners %}
             <li class="nav-item">
-                {% url 'pl-gallery-owner' slug=gallery.slug owner=owner.id as url %}
+                {% url 'photologue:pl-gallery-owner' slug=gallery.slug owner=owner.id as url %}
                 <a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}">
                     {% if owner.get_full_name %}{{ owner.get_full_name }}{% else %}{{ owner.username }}{% endif %}
                 </a>
@@ -72,7 +72,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
         {% endfor %}
     </div>
     <div class="card-footer">
-        <a href="{% url 'gallery-download' gallery.slug %}" class="btn btn-secondary btn-sm">{% trans 'Download all gallery' %}</a>
+        <a href="{% url 'photologue:pl-gallery-download' gallery.slug %}" class="btn btn-secondary btn-sm">{% trans 'Download all gallery' %}</a>
     </div>
 </div>
 {% endblock %}
diff --git a/photologue_custom/urls.py b/photologue_custom/urls.py
index c5fbc20c2a48f0a7d0a5dc7a93fc4a126a4544e4..6000e73817cb212bc150346c0393995834497bb7 100644
--- a/photologue_custom/urls.py
+++ b/photologue_custom/urls.py
@@ -1,15 +1,19 @@
 from django.urls import path, re_path
 
 from .views import (CustomGalleryArchiveIndexView, CustomGalleryDetailView,
-                    CustomGalleryYearArchiveView, GalleryDownload,
-                    GalleryUpload, TagDetail)
+                    CustomGalleryYearArchiveView, CustomPhotoDetailView,
+                    GalleryDownload, GalleryUpload, TagDetail)
 
+# Rather than using photologue default router, we redefine our own router
+# with login and permission checks.
+app_name = 'photologue'
 urlpatterns = [
     path('tag/<slug:slug>/', TagDetail.as_view(), name='tag-detail'),
     path('gallery/', CustomGalleryArchiveIndexView.as_view(), name='pl-gallery-archive'),
     re_path(r'^gallery/(?P<year>\d{4})/$', CustomGalleryYearArchiveView.as_view(), name='pl-gallery-archive-year'),
     path('gallery/<slug:slug>/', CustomGalleryDetailView.as_view(), name='pl-gallery'),
     path('gallery/<slug:slug>/<int:owner>/', CustomGalleryDetailView.as_view(), name='pl-gallery-owner'),
-    path('gallery/<slug:slug>/download/', GalleryDownload.as_view(), name='gallery-download'),
-    path('upload/', GalleryUpload.as_view(), name='gallery-upload'),
+    path('gallery/<slug:slug>/download/', GalleryDownload.as_view(), name='pl-gallery-download'),
+    path('photo/<slug:slug>/', CustomPhotoDetailView.as_view(), name='pl-photo'),
+    path('upload/', GalleryUpload.as_view(), name='pl-gallery-upload'),
 ]
diff --git a/photologue_custom/views.py b/photologue_custom/views.py
index 5355a287471ec4a0f785a09bca95095212dd1d81..81df13dfc723a9dbbd73e1775b0dd7e45e9362d4 100644
--- a/photologue_custom/views.py
+++ b/photologue_custom/views.py
@@ -16,7 +16,8 @@ from django.utils.text import slugify
 from django.views.generic.detail import DetailView
 from django.views.generic.edit import FormView
 from photologue.models import Gallery, Photo
-from photologue.views import GalleryArchiveIndexView, GalleryYearArchiveView
+from photologue.views import (GalleryArchiveIndexView, GalleryYearArchiveView,
+                              PhotoDetailView)
 from PIL import Image
 from taggit.models import Tag
 
@@ -39,7 +40,7 @@ class TagDetail(LoginRequiredMixin, DetailView):
         return context
 
 
-class CustomGalleryArchiveIndexView(GalleryArchiveIndexView):
+class CustomGalleryArchiveIndexView(LoginRequiredMixin, GalleryArchiveIndexView):
     """
     Override to use event date
     """
@@ -47,7 +48,7 @@ class CustomGalleryArchiveIndexView(GalleryArchiveIndexView):
     uses_datetime_field = False  # Fix related object access
 
 
-class CustomGalleryYearArchiveView(GalleryYearArchiveView):
+class CustomGalleryYearArchiveView(LoginRequiredMixin, GalleryYearArchiveView):
     """
     Override to use event date
     """
@@ -55,7 +56,7 @@ class CustomGalleryYearArchiveView(GalleryYearArchiveView):
     uses_datetime_field = False  # Fix related object access
 
 
-class CustomGalleryDetailView(DetailView):
+class CustomGalleryDetailView(LoginRequiredMixin, DetailView):
     """
     Custom gallery detail view to filter on photo owner
     """
@@ -102,13 +103,17 @@ class GalleryDownload(LoginRequiredMixin, DetailView):
         return response
 
 
+class CustomPhotoDetailView(LoginRequiredMixin, PhotoDetailView):
+    pass
+
+
 class GalleryUpload(PermissionRequiredMixin, FormView):
     """
     Form to upload new photos in a gallery
     """
     form_class = UploadForm
     template_name = "photologue/upload.html"
-    success_url = reverse_lazy("gallery-upload")
+    success_url = reverse_lazy("photologue:pl-gallery-upload")
     permission_required = 'photologue.add_gallery'
 
     def form_valid(self, form):