diff --git a/layerindex/forms.py b/layerindex/forms.py
index a9e7703..2cb9b52 100644
--- a/layerindex/forms.py
+++ b/layerindex/forms.py
@@ -118,8 +118,8 @@ class EditLayerForm(StyledModelForm):
 
     def clean_name(self):
         name = self.cleaned_data['name'].strip()
-        if re.compile(r'[^a-z0-9-]').search(name):
-            raise forms.ValidationError("Name must only contain alphanumeric characters and dashes")
+        if re.compile(r'[^a-z0-9-\.]').search(name):
+            raise forms.ValidationError("Name must only contain alphanumeric characters, dashes or periods")
         if name.startswith('-'):
             raise forms.ValidationError("Name must not start with a dash")
         if name.endswith('-'):
diff --git a/layerindex/urls.py b/layerindex/urls.py
index 1fcd11d..80ceaa7 100644
--- a/layerindex/urls.py
+++ b/layerindex/urls.py
@@ -46,7 +46,7 @@ urlpatterns = [
 
     re_path(r'^layers/$',
         RedirectView.as_view(url=reverse_lazy('layer_list', args=('master',)), permanent=False)),
-    re_path(r'^layer/(?P<slug>[-\w]+)/$',
+    re_path(r'^layer/(?P<slug>[-\.\w]+)/$',
         RedirectParamsView.as_view(permanent=False), {'redirect_name': 'layer_item', 'branch': 'master'}),
     re_path(r'^recipes/$',
         RedirectView.as_view(url=reverse_lazy('recipe_search', args=('master',)), permanent=False)),
@@ -65,23 +65,23 @@ urlpatterns = [
         LayerReviewListView.as_view(
             template_name='layerindex/reviewlist.html'),
         name='layer_list_review'),
-    re_path(r'^review/(?P<slug>[-\w]+)/$',
+    re_path(r'^review/(?P<slug>[-\.\w]+)/$',
         LayerReviewDetailView.as_view(
             template_name='layerindex/reviewdetail.html'),
         name='layer_review'),
-    re_path(r'^layer/(?P<slug>[-\w]+)/addnote/$',
+    re_path(r'^layer/(?P<slug>[-\.\w]+)/addnote/$',
         edit_layernote_view, {'template_name': 'layerindex/editlayernote.html'}, name="add_layernote"),
-    re_path(r'^layer/(?P<slug>[-\w]+)/editnote/(?P<pk>[-\w]+)/$',
+    re_path(r'^layer/(?P<slug>[-\.\w]+)/editnote/(?P<pk>[-\w]+)/$',
         edit_layernote_view, {'template_name': 'layerindex/editlayernote.html'}, name="edit_layernote"),
-    re_path(r'^layer/(?P<slug>[-\w]+)/deletenote/(?P<pk>[-\w]+)/$',
+    re_path(r'^layer/(?P<slug>[-\.\w]+)/deletenote/(?P<pk>[-\w]+)/$',
         delete_layernote_view, {'template_name': 'layerindex/deleteconfirm.html'}, name="delete_layernote"),
-    re_path(r'^layer/(?P<slug>[-\w]+)/delete/$',
+    re_path(r'^layer/(?P<slug>[-\.\w]+)/delete/$',
         delete_layer_view, {'template_name': 'layerindex/deleteconfirm.html'}, name="delete_layer"),
     re_path(r'^recipe/(?P<pk>[-\w]+)/$',
         RecipeDetailView.as_view(
             template_name='layerindex/recipedetail.html'),
         name='recipe'),
-    re_path(r'^layer/(?P<name>[-\w]+)/publish/$', publish_view, name="publish"),
+    re_path(r'^layer/(?P<name>[-\.\w]+)/publish/$', publish_view, name="publish"),
     re_path(r'^layerupdate/(?P<pk>[-\w]+)/$',
         LayerUpdateDetailView.as_view(
             template_name='layerindex/layerupdate.html'),
diff --git a/layerindex/urls_branch.py b/layerindex/urls_branch.py
index 40cd915..a9a4018 100644
--- a/layerindex/urls_branch.py
+++ b/layerindex/urls_branch.py
@@ -17,11 +17,11 @@ urlpatterns = [
         LayerListView.as_view(
             template_name='layerindex/layers.html'),
             name='layer_list'),
-    re_path(r'^layer/(?P<slug>[-\w]+)/$',
+    re_path(r'^layer/(?P<slug>[-\.\w]+)/$',
         LayerDetailView.as_view(
             template_name='layerindex/detail.html'),
             name='layer_item'),
-    re_path(r'^layer/(?P<slug>[-\w]+)/recipes/csv/$',
+    re_path(r'^layer/(?P<slug>[-\.\w]+)/recipes/csv/$',
         layer_export_recipes_csv_view,
         name='layer_export_recipes_csv'),
     re_path(r'^recipes/$',
@@ -40,8 +40,8 @@ urlpatterns = [
         ClassSearchView.as_view(
             template_name='layerindex/classes.html'),
             name='class_search'),
-    re_path(r'^edit/(?P<slug>[-\w]+)/$', edit_layer_view, {'template_name': 'layerindex/editlayer.html'}, name="edit_layer"),
-    re_path(r'^update/(?P<slug>[-\w]+)/$', update_layer_view, {'template_name': 'layerindex/updatelayer.html'}, name="update_layer"),
+    re_path(r'^edit/(?P<slug>[-\.\w]+)/$', edit_layer_view, {'template_name': 'layerindex/editlayer.html'}, name="edit_layer"),
+    re_path(r'^update/(?P<slug>[-\.\w]+)/$', update_layer_view, {'template_name': 'layerindex/updatelayer.html'}, name="update_layer"),
     re_path(r'^duplicates/$',
         DuplicatesView.as_view(
             template_name='layerindex/duplicates.html'),
