diff --git a/app/models/communication/website/category.rb b/app/models/communication/website/category.rb
index 15d4c5c5e154f5d9d577849f28864a27cc2d44ac..b64503042ce45abb936f5a941c4effdec037e89d 100644
--- a/app/models/communication/website/category.rb
+++ b/app/models/communication/website/category.rb
@@ -45,6 +45,7 @@ class Communication::Website::Category < ApplicationRecord
   include WithSlug # We override slug_unavailable? method
   include WithTree
   include WithPosition
+  include WithWebsitePermalink
 
   has_one                 :imported_category,
                           class_name: 'Communication::Website::Imported::Category',
@@ -106,6 +107,10 @@ class Communication::Website::Category < ApplicationRecord
     self.class.unscoped.where(parent: parent, university: university, website: website).where.not(id: id)
   end
 
+  def computed_permalink_for_website(website)
+    raw_permalink_for_website(website).gsub(':slug', self.path)
+  end
+
   protected
 
   def last_ordered_element
@@ -123,4 +128,8 @@ class Communication::Website::Category < ApplicationRecord
   def inherited_blob_ids
     [best_featured_image&.blob_id]
   end
+
+  def permalink_config_key
+    :categories
+  end
 end
diff --git a/app/models/communication/website/configs/permalinks.rb b/app/models/communication/website/configs/permalinks.rb
index cf836fd47e1a754b79dd1922b498f0d4b5c0e929..4c5176baf8b55d76ecc8459df1a2dcf66c32a73e 100644
--- a/app/models/communication/website/configs/permalinks.rb
+++ b/app/models/communication/website/configs/permalinks.rb
@@ -43,4 +43,22 @@ class Communication::Website::Configs::Permalinks < Communication::Website
     "admin/communication/websites/configs/permalinks/static"
   end
 
+  def permalinks_data
+    @permalinks_data ||= begin
+      data = {}
+      data[:posts] = "#{self.special_page(:communication_posts).path_without_language}:year/:month/:day/:slug/" if has_communication_posts?
+      data[:categories] = "#{self.special_page(:communication_posts).path_without_language}:slug/" if has_communication_posts? && has_communication_categories?
+      data[:persons] = "#{self.special_page(:persons).path_without_language}:slug/" if has_persons?
+      data[:organizations] = "#{self.special_page(:organizations).path_without_language}:slug/" if has_organizations?
+      # website might have authors but no communication_posts (if a post unpublished exists)
+      data[:authors] = "#{self.special_page(:persons).path_without_language}:slug/#{self.special_page(:communication_posts).slug}/" if has_authors? && has_communication_posts?
+      data[:diplomas] = "#{self.special_page(:education_diplomas).path_without_language}:slug/" if has_education_diplomas?
+      # ces paths complémentaires sont nécessaires à Hugo mais on ne les utilise pas
+      data[:administrators] = "#{self.special_page(:persons).path_without_language}:slug/roles/" if has_administrators?
+      data[:teachers] = "#{self.special_page(:persons).path_without_language}:slug/programs/" if has_teachers?
+      data[:researchers] = "#{self.special_page(:persons).path_without_language}:slug/papers/" if has_researchers?
+      data
+    end
+  end
+
 end
diff --git a/app/models/communication/website/page.rb b/app/models/communication/website/page.rb
index 514b34a3828b6390e82d7626786a258de389f855..29e2add7d11a578cd2588a6fb44f8406f8745cef 100644
--- a/app/models/communication/website/page.rb
+++ b/app/models/communication/website/page.rb
@@ -41,6 +41,7 @@
 #
 
 class Communication::Website::Page < ApplicationRecord
+  include Accessible
   include Sanitizable
   include WithUniversity
   include WithBlobs
@@ -52,7 +53,7 @@ class Communication::Website::Page < ApplicationRecord
   include WithPosition
   include WithTree
   include WithPath
-  include Accessible
+  include WithWebsitePermalink
 
   has_summernote :text
 
@@ -115,7 +116,7 @@ class Communication::Website::Page < ApplicationRecord
   end
 
   def full_width
-    kind_home?  ? true 
+    kind_home?  ? true
                 : attributes['full_width']
   end
 
@@ -140,6 +141,10 @@ class Communication::Website::Page < ApplicationRecord
               .where.not(id: id)
   end
 
+  def computed_permalink_for_website(website)
+    path
+  end
+
   protected
 
   def check_accessibility
diff --git a/app/models/communication/website/post.rb b/app/models/communication/website/post.rb
index 83ffa586032791b5fcf8313ea506bbceec756a39..50488f275d671640b2ed4a47831b661549f32f6d 100644
--- a/app/models/communication/website/post.rb
+++ b/app/models/communication/website/post.rb
@@ -43,6 +43,7 @@ class Communication::Website::Post < ApplicationRecord
   include WithBlocks
   include WithMenuItemTarget
   include WithSlug # We override slug_unavailable? method
+  include WithWebsitePermalink
 
   has_summernote :text
 
@@ -142,6 +143,13 @@ class Communication::Website::Post < ApplicationRecord
     "#{website.url}#{website.special_page(:communication_posts).path}#{path}"
   end
 
+  def computed_permalink_for_website(website)
+    return unless website.id == communication_website_id && published && published_at
+    raw_permalink_for_website(website)
+      .gsub(':slug', self.slug)
+      .gsub(':year/:month/:day', published_at.strftime("%Y/%m/%d"))
+  end
+
   def to_s
     "#{title}"
   end
@@ -174,4 +182,8 @@ class Communication::Website::Post < ApplicationRecord
     end
     author.update_and_sync(is_author: true) if author_id
   end
+
+  def permalink_config_key
+    :posts
+  end
 end
diff --git a/app/models/concerns/with_website_permalink.rb b/app/models/concerns/with_website_permalink.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f3691e458ba44ed0c4daf2a6c1594afb1678be23
--- /dev/null
+++ b/app/models/concerns/with_website_permalink.rb
@@ -0,0 +1,26 @@
+module WithWebsitePermalink
+  extend ActiveSupport::Concern
+
+  included do
+
+    def permalink_for_website(website)
+      computed_permalink = computed_permalink_for_website(website)
+      computed_permalink.present? ? Static.clean_path(computed_permalink) : nil
+    end
+
+    def computed_permalink_for_website(website)
+      raw_permalink_for_website(website).gsub(':slug', self.slug)
+    end
+
+    protected
+
+    def raw_permalink_for_website(website)
+      website.config_permalinks.permalinks_data[permalink_config_key]
+    end
+
+    def permalink_config_key
+      raise NotImplementedError
+    end
+
+  end
+end
diff --git a/app/models/university/person.rb b/app/models/university/person.rb
index ca6f03bb53cd18eed81fd58c44d6a838fef478df..622c91a590a3176f55f118be32eb51e16130e8ae 100644
--- a/app/models/university/person.rb
+++ b/app/models/university/person.rb
@@ -56,6 +56,7 @@ class University::Person < ApplicationRecord
   include WithPicture
   include WithRoles
   include WithBlocks
+  include WithWebsitePermalink
 
   LIST_OF_ROLES = [
     :administration,
@@ -235,4 +236,8 @@ class University::Person < ApplicationRecord
   def prepare_name
     self.name = to_s
   end
+
+  def permalink_config_key
+    :persons
+  end
 end
diff --git a/app/views/admin/communication/websites/configs/permalinks/static.html.erb b/app/views/admin/communication/websites/configs/permalinks/static.html.erb
index 71371e535a9d1a401b0c511dce0bbea6c72a1643..3574b6256c1048e217ade1a62b6e85612633aeeb 100644
--- a/app/views/admin/communication/websites/configs/permalinks/static.html.erb
+++ b/app/views/admin/communication/websites/configs/permalinks/static.html.erb
@@ -1,29 +1,3 @@
-<% if @website.has_communication_posts? %>
-posts:          <%= @website.special_page(:communication_posts).path_without_language %>:year/:month/:day/:slug/
-<% end %>
-<% if @website.has_communication_posts? && @website.has_communication_categories? %>
-categories:     <%= @website.special_page(:communication_posts).path_without_language %>:slug/
-<% end %>
-<% if @website.has_persons? %>
-persons:        <%= @website.special_page(:persons).path_without_language %>:slug/
-<% end %>
-<% if @website.has_organizations? %>
-organizations:  <%= @website.special_page(:organizations).path_without_language %>:slug/
-<% end %>
-<%# website might have authors but no communication_posts (if a post unpublished exists) %>
-<% if @website.has_authors? && @website.has_communication_posts? %>
-authors:        <%= @website.special_page(:persons).path_without_language %>:slug/<%= @website.special_page(:communication_posts).slug %>/
-<% end %>
-<% if @website.has_education_diplomas? %>
-diplomas:       <%= @website.special_page(:education_diplomas).path_without_language %>:slug/
-<% end %>
-<%# ces paths complémentaires sont nécessaires à Hugo mais on ne les utilise pas %>
-<% if @website.has_administrators? %>
-administrators: <%= @website.special_page(:persons).path_without_language %>:slug/roles/
-<% end %>
-<% if @website.has_teachers? %>
-teachers:       <%= @website.special_page(:persons).path_without_language %>:slug/programs/
-<% end %>
-<% if @website.has_researchers? %>
-researchers:    <%= @website.special_page(:persons).path_without_language %>:slug/papers/
-<% end %>
+<% @website.config_permalinks.permalinks_data.each do |key, value| %>
+<%= key %>: <%= value %>
+<% end %>
\ No newline at end of file